BBS-个人站点页

重点功能

  • 侧边栏搜索功能路由整合
  • 判断用户是否存在,用户不存在显示404页面
  • 虚拟字段,年月,使用TruncMonth模块
  • 按照不同的条件进行分组(annotate),显示该分组下的所有文章
  • 根据不同的筛选条件来查询符合的文章

一、添加路由

urls.py文件中

个人站点路由

# 个人站点页面路由
url(r'^(?P<username>\w+)/$', views.site),

我们可以将最左侧的3项过滤栏的路由整合到一起(使用有名分组的形式)

# 侧边栏搜索功能
# ?wupeiqi/tag/1
# url(r'^(?P<username>\w+)/tag/(\d+)$', views.site),  # 标签路由,存标签id
# url(r'^(?P<username>\w+)/category/(\d+)$', views.site),  # 分类路由
# url(r'^(?P<username>\w+)/archive/(\w+)$', views.site),  # 年月档案路由,不同的符号

# 侧边栏搜索功能的整合路由
url(r'^(?P<username>\w+)/(?P<condition>tag|category|archive)/(?P<param>.*)', views.site),  # 优化一下
# django2版本:re_path

分析:

"""
侧边栏点击添加不同的路由:
按照标签筛选
https://www.cnblogs.com/wupeiqi/tag/1/
按照分类筛选
https://www.cnblogs.com/wupeiqi/category/1/
按照时间筛选
https://www.cnblogs.com/wupeiqi/archive/2020-11/

组装侧边栏路由:
https://www.cnblogs.com/站点名/tag/标签id/
https://www.cnblogs.com/站点名/category/分类id/
https://www.cnblogs.com/站点名/archive/年-月/
视图函数仍然是site(),只是展示的文章少了(符合条件的文章数少了),即视图函数中要在查询之前先根据条件筛选一下文章。
"""

二、个人站点前端

1、base.html

步骤:

1.三七分,左边三分写导航栏,右边七分是文章列表展示
	个人站点标题
2.文章数据,是后端查询当前站点的所有文章
3.调正文章下附带文字,class="pull-right" <!-- 样式在右边显示 -->
	作者名
	注册时间
	点赞数、点踩数、评论数
	注册
4.左边导航栏展示,所需数据需要先从后端获取
    4.1 分类,分类下的所有数量。
    4.2 标签,标签下的所有文章数量。
    4.3 年月时间,符合的所有文章数量。
5. 左边的导航栏中的所有地址跳转
    5.1 分类跳转路由:站点名(与用户名一致)/分类category/分类id
    5.2 标签跳转路由:文章站点名(与用户名一致)/标签tag/标签id
    5.3 标签跳转路由:文章站点名(与用户名一致)/日期archive/y-m

base页面

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>父继承页面</title>
    {#    <link rel="stylesheet" href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.4.1/css/bootstrap.min.css">#}
    {#    <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.4/jquery.min.js"></script>#}
    {#    <script src="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.4.1/css/bootstrap.min.css"></script>#}
    {% load static %}<!--引入静态文件-->
    <link rel="stylesheet" href="{% static 'bootstrap-3.4.1-dist/css/bootstrap.min.css' %}">
    <script src="{% static 'js/jquery.min.js' %}"></script>
    <script src="{% static 'bootstrap-3.4.1-dist/js/bootstrap.min.js' %}"></script>
    <script src="{% static 'layer/layer.js' %}"></script>
    {% block css %}
    
    {% endblock %}
</head>
<body>
{# 导航条开始 #}
<!-- 跟home网页一样的导航条 -->
<nav class="navbar navbar-inverse">
    <div class="container-fluid">
        <!-- Brand and toggle get grouped for better mobile display -->
        <div class="navbar-header">
            <button type="button" class="navbar-toggle collapsed" data-toggle="collapse"
                    data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
                <span class="sr-only">Toggle navigation</span>
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
            </button>
            <a class="navbar-brand" href="#">{{ blog.site_title}}</a><!-- 个人站点标题 -->
        </div>
        <!-- Collect the nav links, forms, and other content for toggling -->
        <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
            <ul class="nav navbar-nav">
                <li class="active"><a href="#">首页 <span class="sr-only">(current)</span></a></li>
                <li><a href="#">新闻</a></li>
                <li class="dropdown">
                    <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true"
                       aria-expanded="false">其他选择 <span class="caret"></span></a>
                    <ul class="dropdown-menu">
                        <li><a href="#">专区</a></li>
                        <li><a href="#">闪存</a></li>
                        <li><a href="#">班级</a></li>
                        <li role="separator" class="divider"></li>
                        <li><a href="#">怀旧</a></li>
                        <li role="separator" class="divider"></li>
                        <li><a href="#">发现</a></li>
                    </ul>
                </li>
            </ul>
            <form class="navbar-form navbar-left">
                <div class="form-group">
                    <input type="text" class="form-control" placeholder="Search">
                </div>
                <button type="submit" class="btn btn-default">搜索</button>
            </form>
            {# 右侧展示用户名开始 #}
            <ul class="nav navbar-nav navbar-right">
                {% if  request.session.username %} <!-- 用户登录显示用户名,失败显示注册登录样式 -->
                    <li><a href="#">{{ request.session.username }}</a></li> <!-- 动态获取用户名 -->
                    <li class="dropdown">
                        <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true"
                           aria-expanded="false">更多选择 <span class="caret"></span></a>
                        <ul class="dropdown-menu">
                            <li><a href="#" data-toggle="modal" data-target=".bs-example-modal-lg">修改密码</a></li>
                            <li><a href="#">后台管理</a></li>
                            <li><a href="#">退出登录</a></li>
                        </ul>
                    </li>
                {% else %}
                    <li><a href="/register/">注册</a></li><!-- 点击注册跳转到注册页面 -->
                    <li><a href="/login/">登录</a></li>
                {% endif %}
            </ul>
            {# 右侧展示用户名结束 #}

            {# 修改密码模态框开始 #}
            <div><!-- 点击修改密码,弹出模态框 -->
                <div class="modal fade bs-example-modal-lg" tabindex="-1" role="dialog"
                     aria-labelledby="myLargeModalLabel">
                    <div class="modal-dialog modal-lg" role="document">
                        <div class="modal-content">
                            <div class="row">
                                <h1 class="text-center">修改密码</h1>
                                <div class="col-md-8 col-md-offset-2">
                                    <form action="">
                                        <div class="form-group">
                                            <label for="username">用户名:</label>
                                            <input type="text" id="username" class="form-control" disabled
                                                   value="{{ request.session.username }}"><!-- 用户名显示并且禁用 -->
                                        </div>
                                        <div class="form-group">
                                            <label for="old_password">原密码:</label>
                                            <input type="password" id="old_password" class="form-control">
                                        </div>
                                        <div class="form-group">
                                            <label for="new_password">新密码:</label>
                                            <input type="password" id="new_password" class="form-control">
                                        </div>
                                        <div class="form-group">
                                            <label for="re_password">确认密码:</label>
                                            <input type="password" id="re_password" class="form-control">
                                        </div>
                                        {# <input type="button" class="btn btn-success" value="提交">#}
                                        <button class="btn btn-success btn-block">提交</button>
                                        <!-- button放在form表单中有自动提交的事件,下面script中需要阻止后续事件的执行 -->
                                        <br>
                                        <br>
                                    </form>
                                </div>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
            {# 修改密码模态框结束 #}
        </div><!-- /.navbar-collapse -->
    </div><!-- /.container-fluid -->
</nav>
{# 导航条结束 #}

<!-- 三九分 -->
<div class="container-fluid">
    <div class="row">
        <div class="col-md-3">
            {# 左侧搜索展示开始 #}
            <div class="panel panel-warning">
                <div class="panel-heading">文章分类</div>
                <div class="panel-body">
                    {% for cate in cate_list %}
                        <!-- 跳转到文章站点名(与用户名一致)/分类category/分类id。分类名(符合的文章数量) -->

                        <p>
                            <a href="/{{ username }}/category/{{ cate.2 }}">{{ cate.0 }}({{ cate.1 }})</a>
                        </p>
                    {% endfor %}
                </div>
            </div>
            <div class="panel panel-success">
                <div class="panel-heading">文章标签</div>
                <div class="panel-body">
                    {% for tag in tag_list %}
                        <!-- 跳转到文章站点名(与用户名一致)/标签tag/标签id。标签名(符合的文章数量) -->
                        <p>
                            <a href="/{{ username }}/tag/{{ tag.2 }}">{{ tag.0 }}({{ tag.1 }})</a>
                        </p>
                    {% endfor %}
                </div>
            </div>
            <div class="panel panel-danger">
                <div class="panel-heading">随笔档案</div>
                <div class="panel-body">
                    {% for date in date_list %}
                        <!-- 跳转到文章站点名(与用户名一致)/日期archive/y-m。2023年5月(符合的文章数量) -->
                        <p>
                            <a href="/{{ username }}/archive/{{ data.month|date:'Y-m' }}">{{ date.month|date:'Y年m月' }}({{ date.co }})</a>
                        </p>
                    {% endfor %}
                </div>
            </div>
            {# 左侧搜索展示结束 #}
        </div>
        {# 右侧文章内容开始 #}
        <div class="col-md-9"><!-- 右侧的9分显示文章内容 -->
            {% block content %}
            
            {% endblock %}
        </div>
        {# 右侧文章内容结束 #}
    </div>
</div>

{% block js %}

{% endblock %}
</body>
</html>

2、个人站点页面

templates中的site.html(在此页面中继承了上面所写的base.html)

<!-- 个人站点页面 -->
{% extends  'base.html' %}<!-- 继承base页面 -->

{% block content %}
    <!-- 中间是文本内容,使用媒体对象 -->
    <ul class="media-list">
                {% for article in article_list %}<!-- 循环所有文章对象的列表 -->
                    <li class="media">
                        <h4 class="media-heading"><a href="/{{ username }}/article/{{ article.pk }}">{{ article.title }}</a></h4><!-- 文章标题 -->
                        <div class="media-left">
                            <a href="#">
                                <!-- 显示文章列表中的avatar,使用media文件夹 -->
                                <!-- 文章对象.站点表(反向).用户表(反向) -->
                                <img class="media-object" src="/media/{{ article.blog.userinfo.avatar }}" alt="..."
                                     style="width: 70px;"><!-- 头像 -->
                            </a>
                        </div>
                        <div class="media-body">{{ article.desc }}</div><!-- 文章摘要 -->
                        <br>
                        <div class="pull-right"><!-- 全部右侧显示 -->
                            <!-- posted @ 2020-05-06 09:05 武沛齐 阅读(13888) 评论(50) 推荐(37) 编辑 -->
                            <span>posted @&nbsp;&nbsp;</span>
                            <!-- 文章对象.站点表(反向).用户表(反向) -->
                            <span>{{ article.create_time|date:'Y-m-d' }}&nbsp;&nbsp;</span><!-- 注册时间 -->
                            <span><a href="">{{ article.blog.userinfo.username }}</a></span>&nbsp;&nbsp;<!-- 用户名 -->
                            <span><span class="glyphicon glyphicon-thumbs-up"></span>({{ article.up_num }})&nbsp;&nbsp;</span>
                            <!-- 点赞数 -->
                            <span><span class="glyphicon glyphicon-thumbs-down"></span>({{ article.down_num }})&nbsp;&nbsp;</span>
                            <!-- 点踩数 -->
                            <span><span class="glyphicon glyphicon-comment"></span>({{ article.comment_num }})&nbsp;&nbsp;</span>
                            <!-- 评论数 -->
                            <span><a href="">编辑</a></span>
                        </div>
                        <hr>
                    </li>
                {% endfor %}
            </ul>
{% endblock %}

3、404页面

<html>
<head>
    <meta charset="utf-8">
    <link rel="icon" href="//common.cnblogs.com/favicon.ico" type="image/x-icon">
    <title>404 页面不存在 - BBS</title>
    <style type="text/css">
        body {
            margin: 8% auto 0;
            max-width: 400px;
            min-height: 200px;
            padding: 10px;
            font-family: 'PingFang SC', 'Microsoft YaHei', 'Helvetica Neue', 'Helvetica', 'Arial', sans-serif;
            font-size: 14px;
            padding-right: 200px;
            position: relative;
        }

        p {
            color: #555;
            margin: 15px 0px;
        }

        img {
            border: 0px;
        }

        .d {
            color: #404040;
        }

        .robot img {
            max-width: 192px;
        }

        .robot {
            position: absolute;
            top: 0;
            right: 0;
        }
    </style>
</head>
<body>
<!-- 404页面可以直接复制 -->
<!--把两张照片复制下来-->
<p style="margin-left: 5px;"><a href="https://www.cnblogs.com/"><img src="/static/img/logo.svg"style="height:45px" alt="cnblogs"></a></p>

<div style="margin-top:20px">
    <p style=""><b style="">404.</b> 抱歉,您访问的资源不存在。</p>
    <p class="d">可能是网址有误,或者对应的内容被删除,或者处于私有状态。</p>
    <p style="color:#777;">代码改变世界,联系邮箱 contact@cnblogs.com</p>
    <p><a href="https://www.cnblogs.com/cmt/p/17320765.html">园子的商业化努力-困境求助:开设捐助通道</a></p>
</div>
<div class="robot"><a href="//www.cnblogs.com/cmt/articles/13940458.html"><img
        src="/static/img/img.png" alt="404 robot"></a></div>
<script async="" src="https://www.googletagmanager.com/gtag/js?id=G-4CQQXWHK3C"></script>
<script>
    window.dataLayer = window.dataLayer || [];

    function gtag() {
        dataLayer.push(arguments);
    }

    gtag('js', new Date());

    gtag('config', 'G-4CQQXWHK3C');
</script>

</body>
</html>

图片防盗链

""""
图片防盗链 -- 404页面
在请求头里面有个参数 referer参数可以区分出地址是从哪来的
referer参数指代的是上一次访问的路径地址,就是你这个地址是从哪过来的

怎么解决?直接把图片下载好后保存自己的项目的静态文件中,路径写静态文件的地址。
"""

三、添加视图函数

1、站点后端步骤

1.先根据用户名查询用户表
2. 用户不存在即站点不存咋,显示404页面
3. 查询出当前站点对象并赋值,供后续的筛选条件
4. 查询当前站点下的所有文章,前端9份展示
# (5-7步)前端导航条要展示的数据查询
5. 查询当前站点下的所有分类和统计的文章数(分类表查文章,反向),annocate分组、Count计数、values()列表套字典
6. 查询当前站点下的所有标签和统计的文章数(标签表查文章,反向),annocate分组、Count计数、values_list()列表套元组
7. 查询当前站点下的所有文章,并且按照年月展示,统计数量(文章表)
	TruncMonth:统计年月的模块
    先按照年月分组,annocate,虚拟字段
    .values('month').annotate():按照虚拟字段中的月分组
    选择月份和计数,values()列表套字典
8. 根据kwargs来筛选符合的文章
    8.1 判断kwargs,有值就代表筛选,没有值就是所有文章
    8.2 获取kwargs中的两个值
    8.3 先按照condition条件筛选,分类、标签、年月
    8.4 再按照具体的参数进一步筛选
        文章查分类表 --> 反向(表名小写__字段)
        文章查标签表 --> 正向(外键字段名__字段)
        年月:解压赋值年月,单表查询,基于双下划线查询,(字段__year)

2.虚拟字段:年月

"""
id   title    content  create_time        month
1       a       a        2023-05-10       2023-05
官方提供的TruncMonth模块是创建一个年月的虚拟字段
"""

-官方提供

from django.db.models.functions import TruncMonth

Article.objects
.annotate(month=TruncMonth('create_time'))  # Truncate to month and add to select list  # 括号内写存时间的字段,截断到月份并添加到选择列表
.values('month')  # Group By month  # 按照年月分组
.annotate(c=Count('id'))  # Select the count of the grouping  # 按照月分组计数
.values('month', 'c')  # (might be redundant, haven't tested) select month and count  # 选择月份和计数

3.函数代码

from django.db.models import Count


@login_auth
# def site(request, username):
def site(request, username, **kwargs):  # 后面的不同参数直接用kwargs接收
    """个人站点页面显示函数"""
    # 先根据用户名查询用户信息,用户表
    user_obj = models.UserInfo.objects.filter(username=username).first()
    if not user_obj:  # 站点不存在,直接显示404页面
        return render(request, '404.html')

    # 站点对象(用户表->站点表-->正向)
    blog_obj = user_obj.blog  # 站点信息要先查询出当前用户的站点对象
    # print(blog_obj)  # Blog object

    # 查询当前站点的所有文章
    article_list = models.Article.objects.filter(blog=blog_obj).all()

    """筛选符合的文章"""
    if kwargs:  # 有值就代表筛选,没有值就是所有文章
        """
        点击标签的字典:{'condition': 'tag', 'param': '1'}
        点击分类的字典:{'condition': 'category', 'param': '1'}
        点击年月的字典:{'condition': 'archive', 'param': '2020-11'}
        """
        condition = kwargs.get('condition')
        param = kwargs.get('param')
        # 按照condition条件先筛选
        if condition == 'category':  # 按照分类id进一步筛选,筛选文章
            # 文章查分类表 --> 反向(表名小写__字段)
            article_list = article_list.filter(category__pk=param)
        elif condition == 'tag':
            # 文章查标签表 --> 正向(外键字段名__字段)
            article_list = article_list.filter(tags__pk=param)
        else:
            year, month = param.split('-')  # 2020-11  解压赋值:[2020, 11]
            # 单表查询,基于双下划线查询,(字段__year)
            article_list = article_list.filter(create_time__year=year, create_time__month=month)

    # 1.查询当前站点下的所有分类(左侧导航条展示)和分类下的所有文章(右侧点击一个分类,只显示该分类下的文章)

    # 按照分类分组(annotate),然后查询分类下的文章数,并统计
    # 结合聚合函数进行统计,要导入
    # 分类查询文章-->反向查询, 分类名,统计的文章数,分类id
    # cate_list = models.Category.objects.filter(blog=blog_obj).annotate(count_num=Count("article__pk")).values('name', 'count_num')
    # <QuerySet [{'name': 'kevin的分类一', 'count_num': 1}, {'name': 'kevin的分类二', 'count_num': 1}]>
    cate_list = models.Category.objects.filter(blog=blog_obj).annotate(count_num=Count("article__pk")).values_list(
        'name', 'count_num', 'pk')
    # print(cate_list)  # <QuerySet [('kevin的分类一', 1), ('kevin的分类二', 1), ('kevin的分类三', 1)]>
    """
    .values(): QuerySet列表套字典 <QuerySet [{}, {}, {}]>
    .values_list(): QuerySet列表套元组 <QuerySet [(), (), ()]>
    """

    # 2.查询当前站点下的所有标签
    # 标签表查文章表-->反向
    tag_list = models.Tag.objects.filter(blog=blog_obj).annotate(count_num=Count("article__pk")).values_list('name',
                                                                                                             'count_num',
                                                                                                             'pk')
    # <QuerySet [('kevin的标签一', 1), ('kevin的标签二', 1), ('kevin的标签三', 1)]>

    # 3.查询当前站点下的所有文章,并且按照年月展示--随笔档案
    from django.db.models.functions import TruncMonth
    # TruncMonth是统计月的
    # annocate前面写values()是按照括号内的值分类
    date_list = models.Article.objects.filter(blog=blog_obj).annotate(month=TruncMonth('create_time')).values(
        'month').annotate(co=Count("pk")).values('month', 'co')
    # <QuerySet [{'month': datetime.date(2022, 3, 1), 'co': 2}, {'month': datetime.date(2023, 4, 1), 'co': 1}]>

    return render(request, 'site.html', locals())

posted @ 2023-05-14 21:39  星空看海  阅读(8)  评论(0编辑  收藏  举报