BBS项目day04 文章详情页、点赞点菜、评论功能(根评论和子评论)、评论分页之刷新评论页面

news/2024/7/2 23:51:47

一、路由

from django.contrib import admin
from django.urls import path, re_path
from app01 import views
from django.views.static import serve
from django.conf import settings

urlpatterns = [
    path('admin/', admin.site.urls),
    # 注册
    path('register/', views.register),
    # 登录
    path('login/', views.login),
    # 验证码
    path('get_code/', views.get_code),
    # 首页路由
    path('home/', views.home),
    # 退出系统
    path('logout/', views.logout),
    # 修改密码
    path('set_password/', views.set_password),
    # 放开media文件夹
    re_path('media/(?P<path>.*)', serve, {'document_root': settings.MEDIA_ROOT}),

    # 点赞点彩
    path('up_and_down/', views.up_and_down),
    # 评论功能
    path('comment/', views.comment),

    # re_path('(?P<username>\w+)/category/(\d+)', views.site),
    # re_path('(?P<username>\w+)/tag/(\d+)', views.site),
    # re_path('(?P<username>\w+)/archive/(\w+)', views.site),

    # 优化以上三个路由
    re_path('(?P<username>\w+)/(?P<condition>category|tag|archive)/(?P<param>.*)', views.site),
    # 文章详情页
    re_path('(?P<username>\w+)/(?P<article_id>\d+)', views.article_detail),
    
    # 放开media文件夹  \w+ 匹配数字字母下划线
    re_path('(?P<username>\w+)', views.site),
]


二、文章详情页

1.前端

{% extends 'home.html' %}

{% block css %}
    <style>
        .s1 {
            margin-right: 10px;
            color: #999;
        }

        .content {
            font-size: 18px;
            color: #444;
        }

        #div_digg {
            float: right;
            margin-bottom: 10px;
            margin-right: 30px;
            font-size: 12px;
            width: 128px;
            text-align: center;
            margin-top: 10px;
        }

        .diggit {
            float: left;
            width: 46px;
            height: 52px;
            background: url(/static/img/upup.gif) no-repeat;
            text-align: center;
            cursor: pointer;
            margin-top: 2px;
            padding-top: 5px;
        }

        .buryit {
            float: right;
            margin-left: 20px;
            width: 46px;
            height: 52px;
            background: url(/static/img/downdown.gif) no-repeat;
            text-align: center;
            cursor: pointer;
            margin-top: 2px;
            padding-top: 5px;
        }

        .clear {
            clear: both;
        }

        .diggword {
            margin-top: 5px;
            margin-left: 0;
            font-size: 12px;
            color: #808080;
        }

        .clearfix:focus {
            content: '';
            display: block;
            clear: both;
        }
    </style>
{% endblock %}

{% block content %}
    <div class="col-md-3">
        <div class="panel panel-info">
            <div class="panel-heading">文章分类</div>
            <div class="panel-body">
                {% for category in category_list %}
                    <!-- 如果后台用的是values -->
                    <p><a href="/{{ username }}/category/{{ category.2 }}">{{ category.0 }} ({{ category.1 }})</a></p>
                    <!-- 如果后台用的是values -->
                    {#                    <p><a href="/{{ username }}/category/{{ category.pk }}">{{ category.name }} ({{ category.count_article_num }})</a></p>#}
                {% endfor %}
            </div>
        </div>
        <div class="panel panel-success">
            <div class="panel-heading">文章标签</div>
            <div class="panel-body">
                {% for tag in tag_list %}
                    <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 %}
                    {#  <p><a href="">{{ date.0 }} ({{ date.1 }})</a></p> #}
                    <p><a href="/{{ username }}/archive/{{ date.month|date:'Y-m' }}">{{ date.month|date:'Y年m月' }}
                        ({{ date.count_article_nums }})</a></p>
                {% endfor %}
            </div>
        </div>
    </div>

    <div class="col-md-9">
        <h3 style="color: #399ab2;">{{ article_detail.title }}</h3>
        <div class="content">
            {{ article_detail.content|safe }}
        </div>
    </div>

{% endblock %}

{% block js %}
    <script>
        // 11647089,'Digg'分别表示文章id和标志flag
        // 思路1:
        {% comment %}function votePost(id, flag) {
            is_up = flag === 'Digg' ? 0 : 1;
        }{% endcomment %}

        // 思路2:
        $(".active").click(function () {
            let is_up = $(this).hasClass('diggit');
            // 文章id
            var article_id = '{{ article_detail.pk }}';
            var _this = $(this);
            // 发起Ajax请求
            $.ajax({
                url: '/up_and_down/',
                type: 'post',
                data: {is_up: is_up, article_id: article_id, csrfmiddlewaretoken: '{{ csrf_token }}'},
                success: function () {
                    // 将标签放入文本,要么是text,要么是html
                    if (res === 200) {
                        $("#digg_tips").text(res.msg);

                        // 如果是点赞就让点赞数加1,如果是点踩就让点踩数加1
                        // 注意:在text()中加数据就是赋值,空就是获取值
                        let old_num = _this.children().text();
                        // 此时的 old_num 是string类型,需要转类型
                        {#_this.children().text(parseInt(old_num)+1);#}
                        //或者使用 Number
                        _this.children().text(Number(old_num) + 1);
                    } else {
                        $("#digg_tips").html(res.msg);
                    }
                },
            });
        });

    </script>
{% endblock %}


2.后端

# 文章详情页
def article_detail(request, username, article_id):
    print(article_id)  # 1

    user_obj = models.UserInfo.objects.filter(username=username).first()
    print(user_obj)

    if not user_obj:
        '''
            图片防盗链:通过 Referer参数判断,
            通过这个参数就可以知道你当前的地址是从哪个网页调过来的,然后做验证   
        '''
        return render(request, '404.html')

    # 查询用户自己的所有文章(过滤当前站点的文章)
    blog = user_obj.blog
    article_detail = models.Article.objects.filter(pk=article_id).first()

    category_list = models.Category.objects.filter(blog=blog).annotate(
        count_article_num=Count('article__pk')).values_list('name', 'count_article_num', 'pk')

    tag_list = models.Tag.objects.filter(blog=blog).annotate(
        count_article_num=Count('article__pk')).values_list('name', 'count_article_num', 'pk')

    date_list = models.Article.objects.annotate(
        month=TruncMonth('create_time')).values('month').filter(blog=blog).annotate(
        count_article_nums=Count('pk')).values('month', 'count_article_nums')

    # 查询所有的评论列表
    comment_list = models.Comment.objects.filter(article_id=article_id).all()

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


三、点赞点菜

1.前端

{% extends 'home.html' %}

{% block css %}
    <style>
        .s1 {
            margin-right: 10px;
            color: #999;
        }

        .content {
            font-size: 18px;
            color: #444;
        }

        #div_digg {
            float: right;
            margin-bottom: 10px;
            margin-right: 30px;
            font-size: 12px;
            width: 128px;
            text-align: center;
            margin-top: 10px;
        }

        .diggit {
            float: left;
            width: 46px;
            height: 52px;
            background: url(/static/img/upup.gif) no-repeat;
            text-align: center;
            cursor: pointer;
            margin-top: 2px;
            padding-top: 5px;
        }

        .buryit {
            float: right;
            margin-left: 20px;
            width: 46px;
            height: 52px;
            background: url(/static/img/downdown.gif) no-repeat;
            text-align: center;
            cursor: pointer;
            margin-top: 2px;
            padding-top: 5px;
        }

        .clear {
            clear: both;
        }

        .diggword {
            margin-top: 5px;
            margin-left: 0;
            font-size: 12px;
            color: #808080;
        }

        .clearfix:focus {
            content: '';
            display: block;
            clear: both;
        }
    </style>
{% endblock %}

{% block content %}
    <div class="col-md-3">
        <div class="panel panel-info">
            <div class="panel-heading">文章分类</div>
            <div class="panel-body">
                {% for category in category_list %}
                    <!-- 如果后台用的是values -->
                    <p><a href="/{{ username }}/category/{{ category.2 }}">{{ category.0 }} ({{ category.1 }})</a></p>
                    <!-- 如果后台用的是values -->
                    {#                    <p><a href="/{{ username }}/category/{{ category.pk }}">{{ category.name }} ({{ category.count_article_num }})</a></p>#}
                {% endfor %}
            </div>
        </div>
        <div class="panel panel-success">
            <div class="panel-heading">文章标签</div>
            <div class="panel-body">
                {% for tag in tag_list %}
                    <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 %}
                    {#  <p><a href="">{{ date.0 }} ({{ date.1 }})</a></p> #}
                    <p><a href="/{{ username }}/archive/{{ date.month|date:'Y-m' }}">{{ date.month|date:'Y年m月' }}
                        ({{ date.count_article_nums }})</a></p>
                {% endfor %}
            </div>
        </div>
    </div>

    <div class="col-md-9">
        <h3 style="color: #399ab2;">{{ article_detail.title }}</h3>
        <div class="content">
            {{ article_detail.content|safe }}
        </div>

        <!-- 点赞点彩样式开始 -->
        <div class="clearfix">
            <div id="div_digg">
                <div class="diggit active" onclick="votePost(11647089,'Digg')">
                    <span class="diggnum" id="digg_count">{{ article_detail.up_num }}</span>
                </div>
                <div class="buryit active" onclick="votePost(11647089,'Bury')">
                    <span class="burynum" id="bury_count">{{ article_detail.down_num }}</span>
                </div>
                <div class="clear"></div>
                <div class="diggword" id="digg_tips" style="color: red;"></div>
            </div>
        </div>
        <!-- 点赞点彩样式结束 -->

    </div>

{% endblock %}

{% block js %}
    <script>
        // 11647089,'Digg'分别表示文章id和标志flag
        // 思路1:
        {% comment %}function votePost(id, flag) {
            is_up = flag === 'Digg' ? 0 : 1;
        }{% endcomment %}

        // 思路2:
        $(".active").click(function () {
            let is_up = $(this).hasClass('diggit');
            // 文章id
            var article_id = '{{ article_detail.pk }}';
            var _this = $(this);
            // 发起Ajax请求
            $.ajax({
                url: '/up_and_down/',
                type: 'post',
                data: {is_up: is_up, article_id: article_id, csrfmiddlewaretoken: '{{ csrf_token }}'},
                success: function () {
                    // 将标签放入文本,要么是text,要么是html
                    if (res === 200) {
                        $("#digg_tips").text(res.msg);

                        // 如果是点赞就让点赞数加1,如果是点踩就让点踩数加1
                        // 注意:在text()中加数据就是赋值,空就是获取值
                        let old_num = _this.children().text();
                        // 此时的 old_num 是string类型,需要转类型
                        {#_this.children().text(parseInt(old_num)+1);#}
                        //或者使用 Number
                        _this.children().text(Number(old_num) + 1);
                    } else {
                        $("#digg_tips").html(res.msg);
                    }
                },
            });
        });

        
    </script>
{% endblock %}


2.后端

# 点赞点彩
def up_and_down(request):
    '''
    分析点赞点彩的实现逻辑:
        1.必须判断用户是否登陆了。如果没有则在前端页面显示登录
        2.若是第一次登录:
            2.1 点赞数加 1
            2.2 在页面上显示点赞成功
        3.如果已经点击过,就提示不让他再点了
        4.如果是第一次点击,应该在处理哪些逻辑
            4.1 肯定需要在点赞点彩表中增加一条记录
            4.2 还需要更新文章中的up_num或者down_num字段
        5. 取消点赞或者点彩功能-----》收藏
    :param request:
    :return:
    '''
    if request.method == 'POST':
        back_dict = {'code': 200, 'msg': '支持成功'}

        # 1.接收参数
        is_up = request.POST.get('is_UP')  # str
        is_up = json.loads(is_up)
        article_id = request.POST.get('article_id')

        # 2.判断用户是否登录
        if not request.session.get('username'):
            back_dict['code'] = 1400
            back_dict['msg'] = '请先<a href="/login/" style="color: red;">登录</a>'
            return JsonResponse(back_dict)

        # 3.验证参数
        # 4.判断是否已经点赞过了
        res = models.UpAndDown.objects.filter(article_id=article_id, user_id=request.session.get('id')).first()
        if res:
            back_dict['code'] = 1401
            back_dict['msg'] = '你已经支持过了'
            return JsonResponse(back_dict)
        # 5.处理业务逻辑
        # 操作up_and_down, article
        if is_up:
            models.Article.objects.create(pk=article_id).update(up_num=F('up_num') + 1)
        else:
            models.Article.objects.filter(pk=article_id).update(down_num=F('down_num') + 1)
            back_dict['msg'] = '支持成功'

        # 查询出来要么点赞,要么点踩
        models.UpAndDown.objects.create(is_up=is_up, article_id=article_id, user_id=request.session.get('id'))
        return JsonResponse(back_dict)

四、评论功能

1.根评论前端

{% extends 'home.html' %}

{% block css %}
    <style>
        .s1 {
            margin-top: 10px;
            color: #999;
        }

        .content {
            font-size: 16px;
            color: #444;
        }

        #div_digg {
            float: right;
            margin-bottom: 10px;
            margin-right: 30px;
            font-size: 12px;
            width: 128px;
            text-align: center;
            margin-top: 10px;
        }

        .diggit {
            float: left;
            width: 46px;
            height: 52px;
            background: url(/static/img/upup.gif) no-repeat;
            text-align: center;
            cursor: pointer;
            margin-top: 2px;
            padding-top: 5px;
        }

        .buryit {
            float: right;
            margin-left: 20px;
            width: 46px;
            height: 52px;
            background: url(/static/img/downdown.gif) no-repeat;
            text-align: center;
            cursor: pointer;
            margin-top: 2px;
            padding-top: 5px;
        }

        .clear {
            clear: both;
        }

        .diggword {
            margin-top: 5px;
            margin-left: 0;
            font-size: 12px;
            color: #808080;
        }

        {# 父标签塌陷 #}
        .clearfix {
            content: "";
            display: block;
            clear: both;
        }
    </style>
{% endblock %}

{% block content %}
    <div class="col-md-3">
        <!-- 带标题的面板 -->
        <div class="panel panel-success">
            <div class="panel-heading">文章分类</div>
            <div class="panel-body">
                {% for cate in cate_list %}
                    {# <p><a href="">{{ cate.name }}({{ cate.1 }})</a></p> #}
                    <p><a href="/{{ username }}/category/{{ cate.2 }}">{{ cate.0 }}({{ cate.1 }})</a></p>
                {% endfor %}
            </div>
        </div>
        <div class="panel panel-info">
            <div class="panel-heading">文章标签</div>
            <div class="panel-body">
                {% for tag in tag_list %}
                    <!-- tag.0是标签名称 tag.1是标签数量 -->
                    {# <p><a href="/{{ username }}/tag/{{ tag.2 }}">{{ tag.0 }}({{ tag.1 }})</a></p> #}
                    <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 %}
                    <p>
                        <a href="/{{ username }}/archive/{{ date.month|date:'Y-m' }}">{{ date.month|date:'Y年m月' }}({{ date.c }})</a>
                    </p>
                {% endfor %}
            </div>
        </div>
    </div>

    <div class="col-md-9">
        <h3 style="color: #9cba39"><a href="">{{ article_detail.title }}</a></h3>
        <div class="content">
            {{ article_detail.content|safe }}
        </div>

        <!-- 点赞点踩样式开始 -->
        <div class="clearfix">
            <div id="div_digg">
                <!-- 点赞 -->
                <div class="diggit active" onclick="votePost(12834355,'Digg')">
                    <span class="diggnum" id="digg_count">{{ article_detail.up_num }}</span>
                </div>
                <!-- 点踩 -->
                <div class="buryit active" onclick="votePost(12834355,'Bury')">
                    <span class="burynum" id="bury_count">{{ article_detail.down_num }}</span>
                </div>
                <div class="clear"></div>
                <div class="diggword" id="digg_tips" style="color: red;"></div>
            </div>
        </div>
        <!-- 点赞点踩样式结束 -->

        <!-- 评论列表的展示开始 -->
        <div class="comment_list">
            <h4><span class="glyphicon glyphicon-comment"></span>评论列表</h4>
            <ul class="list-group">
                {% for comment in comment_list %}
                    <li class="list-group-item">
                        <span style="margin-right: 10px;">#{{ forloop.counter }}楼</span>
                        <span style="margin-right: 10px;">{{ comment.comment_time }}</span>
                        <span style="margin-right: 10px;">{{ comment.user.username }}</span>
                        <span style="margin-right: 10px;" class="pull-right"><a href="#">回复</a></span>

                        <div class="content" style="margin-left: 14px;">
                            你好啊
                        </div>
                    </li>
                {% endfor %}
            </ul>
        </div>
        <!-- 评论列表的展示结束 -->

        <!-- 评论功能开始 -->
        <div class="comment">
            <p><span class="glyphicon glyphicon-comment">发表评论</span></p>
            <p>
                <textarea name="" id="content" cols="30" rows="10"></textarea>
            </p>
            <p>
                <button class="btn btn-success btn_comment">提交评论</button>
            </p>
        </div>
        <!-- 评论功能结束 -->

    </div>
{% endblock %}

{% block js %}
    <script>
        <!--点赞点踩样式开始 -->
        {% comment %}function onclick(id, flag) {
            // 方式1:
            {#if (flag === "Digg") {} else {}#}
            // 方式2:三元表达式
            {#var is_up = flag === 'Digg' ? 0 : 1;#}
        }{% endcomment %}

        // 方式3:给class加上active绑定点击事件
        $(".active").click(function () {
            // 判断是点赞还是点踩
            let is_up = $(this).hasClass("diggit");
            // 文章id
            var article_id = '{{ article_detail.pk }}'

            var _this = $(this);

            // 发起Ajax请求
            $.ajax({
                url: '/up_and_down/',
                type: 'post',
                data: {
                    is_up: is_up,
                    article_id: article_id,
                    csrfmiddlewaretoken: '{{ csrf_token }}'
                },
                success: function (res) {
                    if (res.code === 200) {
                        // 将文本放入标签中
                        $("#digg_tips").text(res.msg);

                        // 如果是点赞,就让点赞数加1,如果是点踩,就让点踩数加1
                        // 先获取文本, 并且将字符串数字转为整型
                        {#let old_num = parseInt(_this.children().text());#}
                        // 或者直接使用Number
                        let old_num = Number(_this.children().text());
                        // 再给文本赋值
                        _this.children().text(old_num + 1);
                    } else {
                        $("#digg_tips").html(res.msg)
                    }
                }
            });
        });
        <!-- 点赞点踩样式结束 -->

        <!-- 评论功能开始 -->
        $(".btn_comment").click(function () {
            {#alert(123);#}
            // 1.获取参数
            // 获取文章id
            var article_id = '{{ article_detail.pk }}';
            // 获取文章内容
            let content = $("#content").val();

            // 2.发起Ajax请求
            $.ajax({
                url: '/comment/',
                type: 'post',
                data: {
                    article_id: article_id,
                    content: content,
                    csrfmiddlewaretoken: '{{ csrf_token }}'
                },
                success: function (res) {
                    // 评论成功后,把评论内容显示出来
                    // 首先要做评论内容的临时渲染
                    // 反引号是ES6中的模板语法
                    var username = '{{ request.session.username }}';
                    let html = `<li class="list-group-item">
                        <span style="margin-right: 10px;"><span class="glyphicon glyphicon-comment"></span>${username}</span>
                        <div class="content" style="margin-left: 14px;">
                            ${content}
                        </div>
                    </li>`;
                    $(".list-group").append(html);
                    // 清空评论框中的内容,即赋值为空(字符串空)
                    $("#content").val('');
                }
            });
        });
        <!-- 评论功能结束 -->

    </script>
{% endblock %}

2.根评论后端


# 评论功能
def comment(request):
    '''
    分析评论的逻辑:
        1.登录之后才能评论
        2.评论的内容要入库
            1.操作文章表,
            2.评论表
    :param request:
    :return:
    '''
    back_dict = {'code': 200, 'msg': '支持成功'}

    # 1.接收参数
    article_id = request.POST.get('article_id')
    content = request.POST.get('content')
    parent_id = request.POST.get('parent_id')

    # 2.判断用户是否登录
    if not request.session.get('username'):
        back_dict['code'] = 1404
        back_dict['msg'] = '请先登录之后再评论'
        return JsonResponse(back_dict)

    # 加事务
    from django.db import transaction
    try:
        with transaction.atomic():
            # 操作文章表
            models.Article.objects.filter(pk=article_id).update(comment_num=F('comment_num') + 1)
            # 操作评论表
            models.Comment.objects.create(content=content, article_id=article_id,
                                          parent_id=parent_id, user_id=request.session.get('id'))
    except:
        # 加入日志
        ...
        transaction.rollback()

    return JsonResponse(back_dict)

3.子评论前端

{% extends 'home.html' %}

{% block css %}
    <style>
        .s1 {
            margin-top: 10px;
            color: #999;
        }

        .content {
            font-size: 16px;
            color: #444;
        }

        #div_digg {
            float: right;
            margin-bottom: 10px;
            margin-right: 30px;
            font-size: 12px;
            width: 128px;
            text-align: center;
            margin-top: 10px;
        }

        .diggit {
            float: left;
            width: 46px;
            height: 52px;
            background: url(/static/img/upup.gif) no-repeat;
            text-align: center;
            cursor: pointer;
            margin-top: 2px;
            padding-top: 5px;
        }

        .buryit {
            float: right;
            margin-left: 20px;
            width: 46px;
            height: 52px;
            background: url(/static/img/downdown.gif) no-repeat;
            text-align: center;
            cursor: pointer;
            margin-top: 2px;
            padding-top: 5px;
        }

        .clear {
            clear: both;
        }

        .diggword {
            margin-top: 5px;
            margin-left: 0;
            font-size: 12px;
            color: #808080;
        }

        {# 父标签塌陷 #}
        .clearfix {
            content: "";
            display: block;
            clear: both;
        }
    </style>
{% endblock %}

{% block content %}
    <div class="col-md-3">
        <!-- 带标题的面板 -->
        <div class="panel panel-success">
            <div class="panel-heading">文章分类</div>
            <div class="panel-body">
                {% for cate in cate_list %}
                    {# <p><a href="">{{ cate.name }}({{ cate.1 }})</a></p> #}
                    <p><a href="/{{ username }}/category/{{ cate.2 }}">{{ cate.0 }}({{ cate.1 }})</a></p>
                {% endfor %}
            </div>
        </div>
        <div class="panel panel-info">
            <div class="panel-heading">文章标签</div>
            <div class="panel-body">
                {% for tag in tag_list %}
                    <!-- tag.0是标签名称 tag.1是标签数量 -->
                    {# <p><a href="/{{ username }}/tag/{{ tag.2 }}">{{ tag.0 }}({{ tag.1 }})</a></p> #}
                    <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 %}
                    <p>
                        <a href="/{{ username }}/archive/{{ date.month|date:'Y-m' }}">{{ date.month|date:'Y年m月' }}({{ date.c }})</a>
                    </p>
                {% endfor %}
            </div>
        </div>
    </div>

    <div class="col-md-9">
        <h3 style="color: #9cba39"><a href="">{{ article_detail.title }}</a></h3>
        <div class="content">
            {{ article_detail.content|safe }}
        </div>

        <!-- 点赞点踩样式开始 -->
        <div class="clearfix">
            <div id="div_digg">
                <!-- 点赞 -->
                <div class="diggit active" onclick="votePost(12834355,'Digg')">
                    <span class="diggnum" id="digg_count">{{ article_detail.up_num }}</span>
                </div>
                <!-- 点踩 -->
                <div class="buryit active" onclick="votePost(12834355,'Bury')">
                    <span class="burynum" id="bury_count">{{ article_detail.down_num }}</span>
                </div>
                <div class="clear"></div>
                <div class="diggword" id="digg_tips" style="color: red;"></div>
            </div>
        </div>
        <!-- 点赞点踩样式结束 -->

        <!-- 评论列表的展示开始 -->
        <div class="comment_list">
            <h4><span class="glyphicon glyphicon-comment"></span>评论列表</h4>
            <ul class="list-group">
                {% for comment in comment_list %}
                    <li class="list-group-item">
                        <span style="margin-right: 10px;">#{{ forloop.counter }}</span>
                        <span style="margin-right: 10px;">{{ comment.comment_time }}</span>
                        <span style="margin-right: 10px;">{{ comment.user.username }}</span>
                        <!-- href="javascript;" 防止页面跳转,这是由于锚点的缘故,若是href="" 会自动刷新页面到最上面 -->
                        <span style="margin-right: 10px;" class="pull-right">
                            <a href="javascript:;" comment_username="{{ comment.user.username }}"
                               comment_id="{{ comment.pk }}" class="reply">回复</a>
                        </span>

                        <div class="content" style="margin-left: 14px;">
                            <!-- 若是根评论则直接显示评论内容,子评论需要@+当前评论者 -->
                            {% if comment.parent %}
                                {{ comment.content }}
                            {% else %}
                                <p>@ {{ comment.parent.user.username }}</p>
                                {{ comment.content }}
                            {% endif %}
                        </div>
                    </li>
                {% endfor %}
            </ul>
        </div>
        <!-- 评论列表的展示结束 -->

        <!-- 评论功能开始 -->
        <div class="comment">
            <p><span class="glyphicon glyphicon-comment">发表评论</span></p>
            <p>
                <textarea name="" id="content" cols="30" rows="10"></textarea>
            </p>
            <p>
                <button class="btn btn-success btn_comment">提交评论</button>
            </p>
        </div>
        <!-- 评论功能结束 -->

    </div>
{% endblock %}

{% block js %}
    <script>
        {#<!--点赞点踩样式开始 -->#}
        {% comment %}function onclick(id, flag) {
            // 方式1:
            {#if (flag === "Digg") {} else {}#}
            // 方式2:三元表达式
            {#var is_up = flag === 'Digg' ? 0 : 1;#}
        }{% endcomment %}

        // 方式3:给class加上active绑定点击事件
        $(".active").click(function () {
            // 判断是点赞还是点踩
            let is_up = $(this).hasClass("diggit");
            // 文章id
            var article_id = '{{ article_detail.pk }}'

            var _this = $(this);

            // 发起Ajax请求
            $.ajax({
                url: '/up_and_down/',
                type: 'post',
                data: {
                    is_up: is_up,
                    article_id: article_id,
                    csrfmiddlewaretoken: '{{ csrf_token }}'
                },
                success: function (res) {
                    if (res.code === 200) {
                        // 将文本放入标签中
                        $("#digg_tips").text(res.msg);

                        // 如果是点赞,就让点赞数加1,如果是点踩,就让点踩数加1
                        // 先获取文本, 并且将字符串数字转为整型
                        {#let old_num = parseInt(_this.children().text());#}
                        // 或者直接使用Number
                        let old_num = Number(_this.children().text());
                        // 再给文本赋值
                        _this.children().text(old_num + 1);
                    } else {
                        $("#digg_tips").html(res.msg)
                    }
                }
            });
        });
        <!-- 点赞点踩样式结束 -->

        // 定义一个全局变量,若是根评论值就是null,若是子评论,就给parent_id赋值
        var parent_id = null;

        <!-- 根评论功能开始 -->
        $(".btn_comment").click(function () {
            {#alert(123);#}
            // 1.获取参数
            // 获取文章id
            var article_id = '{{ article_detail.pk }}';
            // 获取文章内容
            let content = $("#content").val();

            if (parent_id) {
                // parent_id有值就代表是子评论,把评论的内容截取掉
                // indexOf() 是匹配到了,就返回它在字符串中的位置, 并把该值之前的内容全部截掉
                let indexOf_num = content.indexOf('\n');
                // 使用slice方法从indexOf_num这个位置开始截取,剩下的返回给content
                content = content.slice(indexOf_num);
            }

            // 2.发起Ajax请求
            $.ajax({
                url: '/comment/',
                type: 'post',
                data: {
                    article_id: article_id,
                    content: content,
                    parent_id: parent_id,
                    csrfmiddlewaretoken: '{{ csrf_token }}'
                },
                success: function (res) {
                    // 评论成功后,把评论内容显示出来
                    // 首先要做评论内容的临时渲染
                    // 反引号是ES6中的模板语法
                    var username = '{{ request.session.username }}';
                    let html = `<li class="list-group-item">
                        <span style="margin-right: 10px;"><span class="glyphicon glyphicon-comment"></span>${username}</span>
                        <div class="content" style="margin-left: 14px;">
                            ${content}
                        </div>
                    </li>`;
                    $(".list-group").append(html);
                    // 清空评论框中的内容,即赋值为空(字符串空)
                    $("#content").val('');
                }
            });
        });
        <!-- 根评论功能结束 -->

        <!-- 子评论功能开始 -->
        $(".reply").click(function () {
            // 获取自定义属性comment_username以便获取username
            let comment_username = $(this).attr('comment_username');

            // 给子评论的id赋值
            parent_id = $(this).attr('comment_id');

            // 获取内容,并为其设置格式和焦点,以便在页面点击回复的时候自动选中
            $("#content").val("@" + comment_username + '\n').focus();
        });
        <!-- 子评论功能结束 -->

    </script>
{% endblock %}

子评论后端

# 文章详情
def article_detail(request, username, article_id):
    print(article_id)  # 1

    user_obj = models.UserInfo.objects.filter(username=username).first()
    print('user_obj:', user_obj)

    if not user_obj:
        '''
            图片防盗链:通过 Referer参数判断,
            通过这个参数就可以知道你当前的地址是从哪个网页调过来的,然后做验证   
        '''
        return render(request, '404.html')

    # 查询用户自己的所有文章(过滤当前站点的文章)
    blog = user_obj.blog
    article_detail = models.Article.objects.filter(pk=article_id).first()

    cate_list = models.Category.objects.filter(blog=blog).annotate(
        count_article_num=Count('article__pk')).values_list('name', 'count_article_num', 'pk')
    print(cate_list)  # <QuerySet [('root的分类一', 4, 1), ('root的分类二', 2, 2)]>

    tag_list = models.Tag.objects.filter(blog=blog).annotate(
        count_article_num=Count('article__pk')).values_list('name', 'count_article_num', 'pk')

    date_list = models.Article.objects.annotate(
        month=TruncMonth('create_time')).values('month').filter(blog=blog).annotate(
        count_article_nums=Count('pk')).values('month', 'count_article_nums')

    # 查询所有的评论列表,查询的是当前这篇文章的所有评论列表
    comment_list = models.Comment.objects.filter(article_id=article_id).all()

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


# 文章评论
def comment(request):
    '''
    分析评论的逻辑:
        1.必须登录才能评论
        2.评论的内容要入库
            1.操作文章表
            2.评论表
    :param request:
    :return:
    '''
    if request.method == 'POST':
        # 1.定义返回给前端的json数据格式
        back_dict = {'code': 200, 'msg': '密码修改成功,3秒之后自动跳转页面', 'data': []}

        # 2.接收参数
        article_id = request.POST.get('article_id')
        content = request.POST.get('content')
        parent_id = request.POST.get('parent_id')

        # 3.判断用户是否登录
        if not request.session.get('username'):
            back_dict['code'] = 5001
            back_dict['msg'] = '请先登录之后再评论'
            return JsonResponse(back_dict)

        # 操作表必须同时成功或者同时失败,需要启用事务
        from django.db import transaction
        try:
            with transaction.atomic():
                # 操作文章表
                models.Article.objects.filter(pk=article_id).update(comment_num=F('comment_num') + 1)
                # 评论评论表
                models.Comment.objects.create(content=content, article_id=article_id,
                                              user_id=request.session.get('id'),
                                              parent_id=parent_id)
        except:
            # 4.记录日志
            ctime = datetime.datetime.today()
            logger = get_logger()
            logger.debug('{}在{}注册了账号'.format(request.session.get('username'), ctime))

            transaction.rollback()
        return JsonResponse(back_dict)

五、评论分页

1.前端

{% extends 'home.html' %}

{% block css %}
    <style>
        .s1 {
            margin-top: 10px;
            color: #999;
        }

        .content {
            font-size: 16px;
            color: #444;
        }

        #div_digg {
            float: right;
            margin-bottom: 10px;
            margin-right: 30px;
            font-size: 12px;
            width: 128px;
            text-align: center;
            margin-top: 10px;
        }

        .diggit {
            float: left;
            width: 46px;
            height: 52px;
            background: url(/static/img/upup.gif) no-repeat;
            text-align: center;
            cursor: pointer;
            margin-top: 2px;
            padding-top: 5px;
        }

        .buryit {
            float: right;
            margin-left: 20px;
            width: 46px;
            height: 52px;
            background: url(/static/img/downdown.gif) no-repeat;
            text-align: center;
            cursor: pointer;
            margin-top: 2px;
            padding-top: 5px;
        }

        .clear {
            clear: both;
        }

        .diggword {
            margin-top: 5px;
            margin-left: 0;
            font-size: 12px;
            color: #808080;
        }

        {# 父标签塌陷 #}
        .clearfix {
            content: "";
            display: block;
            clear: both;
        }
    </style>
{% endblock %}

{% block content %}
    <div class="col-md-3">
        <!-- 带标题的面板 -->
        <div class="panel panel-success">
            <div class="panel-heading">文章分类</div>
            <div class="panel-body">
                {% for cate in cate_list %}
                    {# <p><a href="">{{ cate.name }}({{ cate.1 }})</a></p> #}
                    <p><a href="/{{ username }}/category/{{ cate.2 }}">{{ cate.0 }}({{ cate.1 }})</a></p>
                {% endfor %}
            </div>
        </div>
        <div class="panel panel-info">
            <div class="panel-heading">文章标签</div>
            <div class="panel-body">
                {% for tag in tag_list %}
                    <!-- tag.0是标签名称 tag.1是标签数量 -->
                    {# <p><a href="/{{ username }}/tag/{{ tag.2 }}">{{ tag.0 }}({{ tag.1 }})</a></p> #}
                    <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 %}
                    <p>
                        <a href="/{{ username }}/archive/{{ date.month|date:'Y-m' }}">{{ date.month|date:'Y年m月' }}({{ date.c }})</a>
                    </p>
                {% endfor %}
            </div>
        </div>
    </div>

    <div class="col-md-9">
        <h3 style="color: #9cba39"><a href="">{{ article_detail.title }}</a></h3>
        <div class="content">
            {{ article_detail.content|safe }}
        </div>

        <!-- 点赞点踩样式开始 -->
        <div class="clearfix">
            <div id="div_digg">
                <!-- 点赞 -->
                <div class="diggit active" onclick="votePost(12834355,'Digg')">
                    <span class="diggnum" id="digg_count">{{ article_detail.up_num }}</span>
                </div>
                <!-- 点踩 -->
                <div class="buryit active" onclick="votePost(12834355,'Bury')">
                    <span class="burynum" id="bury_count">{{ article_detail.down_num }}</span>
                </div>
                <div class="clear"></div>
                <div class="diggword" id="digg_tips" style="color: red;"></div>
            </div>
        </div>
        <!-- 点赞点踩样式结束 -->

        <!-- 评论列表的展示开始 -->
        <div class="comment_list">
            <h4><span class="glyphicon glyphicon-comment"></span>评论列表</h4>
            <ul class="list-group">
                {% for comment in comment_list %}
                    <li class="list-group-item">
                        <span style="margin-right: 10px;">#{{ forloop.counter }}</span>
                        <span style="margin-right: 10px;">{{ comment.comment_time }}</span>
                        <span style="margin-right: 10px;">{{ comment.user.username }}</span>
                        <!-- href="javascript;" 防止页面跳转,这是由于锚点的缘故,若是href="" 会自动刷新页面到最上面 -->
                        <span style="margin-right: 10px;" class="pull-right">
                            <a href="javascript:;" comment_username="{{ comment.user.username }}"
                               comment_id="{{ comment.pk }}" class="reply">回复</a>
                        </span>

                        <div class="content" style="margin-left: 14px;">
                            <!-- 若是根评论则直接显示评论内容,子评论需要@+当前评论者 -->
                            {% if comment.parent %}
                                {{ comment.content }}
                            {% else %}
                                <p>@ {{ comment.parent.user.username }}</p>
                                {{ comment.content }}
                            {% endif %}
                        </div>
                    </li>
                {% endfor %}
            </ul>
        </div>
        <!-- 评论列表的展示结束 -->

        <!-- 分页功能开始 -->
        <div class="text-center">
            {{ page_obj.page_html|safe }}
        </div>
        <!-- 分页功能结束 -->

        <!-- 评论功能开始 -->
        <div class="comment">
            <p><span class="glyphicon glyphicon-comment">发表评论</span></p>
            <p>
                <textarea name="" id="content" cols="30" rows="10"></textarea>
            </p>
            <p>
                <button class="btn btn-success btn_comment">提交评论</button>
            </p>
        </div>
        <!-- 评论功能结束 -->

    </div>
{% endblock %}

{% block js %}
    <script>
        {#<!--点赞点踩样式开始 -->#}
        {% comment %}function onclick(id, flag) {
            // 方式1:
            {#if (flag === "Digg") {} else {}#}
            // 方式2:三元表达式
            {#var is_up = flag === 'Digg' ? 0 : 1;#}
        }{% endcomment %}

        // 方式3:给class加上active绑定点击事件
        $(".active").click(function () {
            // 判断是点赞还是点踩
            let is_up = $(this).hasClass("diggit");
            // 文章id
            var article_id = '{{ article_detail.pk }}'

            var _this = $(this);

            // 发起Ajax请求
            $.ajax({
                url: '/up_and_down/',
                type: 'post',
                data: {
                    is_up: is_up,
                    article_id: article_id,
                    csrfmiddlewaretoken: '{{ csrf_token }}'
                },
                success: function (res) {
                    if (res.code === 200) {
                        // 将文本放入标签中
                        $("#digg_tips").text(res.msg);

                        // 如果是点赞,就让点赞数加1,如果是点踩,就让点踩数加1
                        // 先获取文本, 并且将字符串数字转为整型
                        {#let old_num = parseInt(_this.children().text());#}
                        // 或者直接使用Number
                        let old_num = Number(_this.children().text());
                        // 再给文本赋值
                        _this.children().text(old_num + 1);
                    } else {
                        $("#digg_tips").html(res.msg)
                    }
                }
            });
        });
        <!-- 点赞点踩样式结束 -->

        // 定义一个全局变量,若是根评论值就是null,若是子评论,就给parent_id赋值
        var parent_id = null;

        <!-- 根评论功能开始 -->
        $(".btn_comment").click(function () {
            {#alert(123);#}
            // 1.获取参数
            // 获取文章id
            var article_id = '{{ article_detail.pk }}';
            // 获取文章内容
            let content = $("#content").val();

            if (parent_id) {
                // parent_id有值就代表是子评论,把评论的内容截取掉
                // indexOf() 是匹配到了,就返回它在字符串中的位置, 并把该值之前的内容全部截掉
                let indexOf_num = content.indexOf('\n');
                // 使用slice方法从indexOf_num这个位置开始截取,剩下的返回给content
                content = content.slice(indexOf_num);
            }

            // 2.发起Ajax请求
            $.ajax({
                url: '/comment/',
                type: 'post',
                data: {
                    article_id: article_id,
                    content: content,
                    parent_id: parent_id,
                    csrfmiddlewaretoken: '{{ csrf_token }}'
                },
                success: function (res) {
                    // 评论成功后,把评论内容显示出来
                    // 首先要做评论内容的临时渲染
                    // 反引号是ES6中的模板语法
                    var username = '{{ request.session.username }}';
                    let html = `<li class="list-group-item">
                        <span style="margin-right: 10px;"><span class="glyphicon glyphicon-comment"></span>${username}</span>
                        <div class="content" style="margin-left: 14px;">
                            ${content}
                        </div>
                    </li>`;
                    $(".list-group").append(html);
                    // 清空评论框中的内容,即赋值为空(字符串空)
                    $("#content").val('');
                }
            });
        });
        <!-- 根评论功能结束 -->

        <!-- 子评论功能开始 -->
        $(".reply").click(function () {
            // 获取自定义属性comment_username以便获取username
            let comment_username = $(this).attr('comment_username');

            // 给子评论的id赋值
            parent_id = $(this).attr('comment_id');

            // 获取内容,并为其设置格式和焦点,以便在页面点击回复的时候自动选中
            $("#content").val("@" + comment_username + '\n').focus();
        });
        <!-- 子评论功能结束 -->

        <!-- 评论分页功能开始 -->
        $(".btn_page").click(function () {
            //alert(123); // 验证后触发了
            // 1.获取当前是第几页
            let current_page = $(this).attr('current_page');
            var article_id = '{{ article_detail.pk }}';

            // 给li增加active类属性
            // '<li class="active"><a href="javascript:;" class="btn_page" current_page="%s">%s</a></li>' % (i, i)
            // 删除上一页的高亮active
            $(".active").removeClass('active');
            $(this).parent().addClass('active');

            // 2.直接发起Ajax请求
            $.ajax({
                url: '/comment_page/',
                type: 'post',
                data: {
                    current_page: current_page,
                    article_id: article_id,
                    csrfmiddlewaretoken: '{{ csrf_token }}'
                },
                success: function (res) {
                    console.log(res);
                    if (res.code === 200) {
                        var html = "";  // 定义一个全局变量html,用于接收li
                        $.each(res.data, function (index, obj) {
                            console.log(obj);
                            html += `<li class="list-group-item">
                                <span style="margin-right: 10px;">#${obj.forloop}楼</span>
                                <span style="margin-right: 10px;">${obj.comment_time}</span>
                                <span style="margin-right: 10px;">${obj.username}</span>
                                <span style="margin-right: 10px;" class="pull-right">
                                    <a href="javascript:;" comment_username="${obj.username}"
                                        comment_id="${obj.pk}" class="reply">回复</a>
                                </span>
                                <div class="content" style="margin-left: 14px;">
                                    <!-- 若是根评论则直接显示评论内容,子评论需要@+当前评论者 -->
                                    ${obj.content}
                                </div>
                            </li>`;
                        })
                        // 将li放到ul中去
                        $(".list-group").html(html);
                    }

                },
            });
        });
        <!-- 评论分页功能结束 -->

    </script>
{% endblock %}

2.后端

# 文章详情
def article_detail(request, username, article_id):
    print(article_id)  # 1

    user_obj = models.UserInfo.objects.filter(username=username).first()
    print('user_obj:', user_obj)

    if not user_obj:
        '''
            图片防盗链:通过 Referer参数判断,
            通过这个参数就可以知道你当前的地址是从哪个网页调过来的,然后做验证   
        '''
        return render(request, '404.html')

    # 查询用户自己的所有文章(过滤当前站点的文章)
    blog = user_obj.blog
    article_detail = models.Article.objects.filter(pk=article_id).first()

    cate_list = models.Category.objects.filter(blog=blog).annotate(
        count_article_num=Count('article__pk')).values_list('name', 'count_article_num', 'pk')
    print(cate_list)  # <QuerySet [('root的分类一', 4, 1), ('root的分类二', 2, 2)]>

    tag_list = models.Tag.objects.filter(blog=blog).annotate(
        count_article_num=Count('article__pk')).values_list('name', 'count_article_num', 'pk')

    date_list = models.Article.objects.annotate(
        month=TruncMonth('create_time')).values('month').filter(blog=blog).annotate(
        count_article_nums=Count('pk')).values('month', 'count_article_nums')

    # 查询所有的评论列表,查询的是当前这篇文章的所有评论列表
    comment_list = models.Comment.objects.filter(article_id=article_id).all()

    # 分页列表功能
    from utils.mypage1 import Pagination
    current_page = request.GET.get('page')
    try:
        current_page = int(current_page)
    except:
        current_page = 1
    all_count = comment_list.count()
    page_obj = Pagination(current_page, all_count, per_page_num=5)
    comment_list = comment_list[page_obj.start: page_obj.end]

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


# 文章评论
def comment(request):
    '''
    分析评论的逻辑:
        1.必须登录才能评论
        2.评论的内容要入库
            1.操作文章表
            2.评论表
    :param request:
    :return:
    '''
    if request.method == 'POST':
        # 1.定义返回给前端的json数据格式
        back_dict = {'code': 200, 'msg': '密码修改成功,3秒之后自动跳转页面', 'data': []}

        # 2.接收参数
        article_id = request.POST.get('article_id')
        content = request.POST.get('content')
        parent_id = request.POST.get('parent_id')

        # 3.判断用户是否登录
        if not request.session.get('username'):
            back_dict['code'] = 5001
            back_dict['msg'] = '请先登录之后再评论'
            return JsonResponse(back_dict)

        # 操作表必须同时成功或者同时失败,需要启用事务
        from django.db import transaction
        try:
            with transaction.atomic():
                # 操作文章表
                models.Article.objects.filter(pk=article_id).update(comment_num=F('comment_num') + 1)
                # 评论评论表
                models.Comment.objects.create(content=content, article_id=article_id,
                                              user_id=request.session.get('id'),
                                              parent_id=parent_id)
        except:
            # 4.记录日志
            ctime = datetime.datetime.today()
            logger = get_logger()
            logger.debug('{}在{}注册了账号'.format(request.session.get('username'), ctime))

            transaction.rollback()
        return JsonResponse(back_dict)


# 评论列表分页功能
def comment_page(request):
    # 根据当前第几页查询当前页的评论列表数据
    if request.method == 'POST':
        back_dict = {'code': 200, 'msg': '查询成功'}

        # 接收参数
        current_page = request.POST.get('current_page')
        try:
            current_page = int(current_page)
        except:
            current_page = 1
        article_id = request.POST.get('article_id')

        # 验证参数
        if not current_page:
            back_dict['code'] = 6001
            back_dict['msg'] = '当前页数据有误或为空'
            JsonResponse(back_dict)


        # 每页显示的条数
        per_page_num = 5.
        start_page = (current_page - 1) * per_page_num
        end_page = current_page * per_page_num
        # 查询评论列表数据 查询出来的结果是queryset对象,只能在模板渲染的时候才能使用,需要使用for循环取出使用
        comment_list = models.Comment.objects.filter(article_id=article_id).all()[start_page: end_page]
        comment_list_obj = []  # [{},{},{}]
        i = 1
        for comment in comment_list:
            comment_list_obj.append({
                'forloop': i,
                'pk': comment.pk,
                'comment_time': comment.comment_time,
                'username': comment.user.username,
                'content': comment.content
            })
            i += 1
        print(comment_list_obj)
        # [{'forloop': 1, 'comment_time': datetime.datetime(2023, 8, 21, 19, 25, 54, 201246),
        # 'username': 'jack', 'content': '@jack\n你怎么不信呢?'}, ]
        back_dict['data'] = comment_list_obj
        return JsonResponse(back_dict)

3.分页的代码

1.原始的分页代码

class Pagination(object):
    def __init__(self, current_page, all_count, per_page_num=2, pager_count=11):
        """
        封装分页相关数据
        :param current_page: 当前页
        :param all_count:    数据库中的数据总条数
        :param per_page_num: 每页显示的数据条数
        :param pager_count:  最多显示的页码个数
        """
        try:
            current_page = int(current_page)
        except Exception as e:
            current_page = 1

        if current_page < 1:
            current_page = 1

        self.current_page = current_page

        self.all_count = all_count
        self.per_page_num = per_page_num

        # 总页码
        all_pager, tmp = divmod(all_count, per_page_num)
        if tmp:
            all_pager += 1
        self.all_pager = all_pager

        self.pager_count = pager_count
        self.pager_count_half = int((pager_count - 1) / 2)

    @property
    def start(self):
        return (self.current_page - 1) * self.per_page_num

    @property
    def end(self):
        return self.current_page * self.per_page_num

    def page_html(self):
        # 如果总页码 < 11个:
        if self.all_pager <= self.pager_count:
            pager_start = 1
            pager_end = self.all_pager + 1
        # 总页码  > 11
        else:
            # 当前页如果<=页面上最多显示11/2个页码
            if self.current_page <= self.pager_count_half:
                pager_start = 1
                pager_end = self.pager_count + 1

            # 当前页大于5
            else:
                # 页码翻到最后
                if (self.current_page + self.pager_count_half) > self.all_pager:
                    pager_end = self.all_pager + 1
                    pager_start = self.all_pager - self.pager_count + 1
                else:
                    pager_start = self.current_page - self.pager_count_half
                    pager_end = self.current_page + self.pager_count_half + 1

        page_html_list = []
        # 添加前面的nav和ul标签
        page_html_list.append('''
                    <nav aria-label='Page navigation>'
                    <ul class='pagination'>
                ''')
        first_page = '<li><a href="?page=%s">首页</a></li>' % (1)
        page_html_list.append(first_page)

        if self.current_page <= 1:
            prev_page = '<li class="disabled"><a href="#">上一页</a></li>'
        else:
            prev_page = '<li><a href="?page=%s">上一页</a></li>' % (self.current_page - 1,)

        page_html_list.append(prev_page)

        for i in range(pager_start, pager_end):
            if i == self.current_page:
                temp = '<li class="active"><a href="?page=%s">%s</a></li>' % (i, i,)
            else:
                temp = '<li><a href="?page=%s">%s</a></li>' % (i, i,)
            page_html_list.append(temp)

        if self.current_page >= self.all_pager:
            next_page = '<li class="disabled"><a href="#">下一页</a></li>'
        else:
            next_page = '<li><a href="?page=%s">下一页</a></li>' % (self.current_page + 1,)
        page_html_list.append(next_page)

        last_page = '<li><a href="?page=%s">尾页</a></li>' % (self.all_pager,)
        page_html_list.append(last_page)
        # 尾部添加标签
        page_html_list.append('''
                                           </nav>
                                           </ul>
                                       ''')
        return ''.join(page_html_list)


2.优化后的页面不再刷新的分页代码

class Pagination(object):
    def __init__(self, current_page, all_count, per_page_num=2, pager_count=11):
        """
        封装分页相关数据
        :param current_page: 当前页
        :param all_count:    数据库中的数据总条数
        :param per_page_num: 每页显示的数据条数
        :param pager_count:  最多显示的页码个数
        """
        try:
            current_page = int(current_page)
        except Exception as e:
            current_page = 1

        if current_page < 1:
            current_page = 1

        self.current_page = current_page

        self.all_count = all_count
        self.per_page_num = per_page_num

        # 总页码
        all_pager, tmp = divmod(all_count, per_page_num)
        if tmp:
            all_pager += 1
        self.all_pager = all_pager

        self.pager_count = pager_count
        self.pager_count_half = int((pager_count - 1) / 2)

    @property
    def start(self):
        return (self.current_page - 1) * self.per_page_num

    @property
    def end(self):
        return self.current_page * self.per_page_num

    def page_html(self):
        # 如果总页码 < 11个:
        if self.all_pager <= self.pager_count:
            pager_start = 1
            pager_end = self.all_pager + 1
        # 总页码  > 11
        else:
            # 当前页如果<=页面上最多显示11/2个页码
            if self.current_page <= self.pager_count_half:
                pager_start = 1
                pager_end = self.pager_count + 1

            # 当前页大于5
            else:
                # 页码翻到最后
                if (self.current_page + self.pager_count_half) > self.all_pager:
                    pager_end = self.all_pager + 1
                    pager_start = self.all_pager - self.pager_count + 1
                else:
                    pager_start = self.current_page - self.pager_count_half
                    pager_end = self.current_page + self.pager_count_half + 1

        page_html_list = []
        # 添加前面的nav和ul标签
        page_html_list.append('''
                    <nav aria-label='Page navigation>'
                    <ul class='pagination'>
                ''')
        first_page = '<li><a href="?page=%s">首页</a></li>' % (1)
        page_html_list.append(first_page)

        if self.current_page <= 1:
            prev_page = '<li class="disabled"><a href="#">上一页</a></li>'
        else:
            prev_page = '<li><a href="?page=%s">上一页</a></li>' % (self.current_page - 1,)

        page_html_list.append(prev_page)

        for i in range(pager_start, pager_end):
            if i == self.current_page:
                temp = '<li class="active"><a href="javascript:;" class="btn_page" current_page="%s">%s</a></li>' % (i,i)
            else:
                temp = '<li><a href="javascript:;" class="btn_page" current_page="%s">%s</a></li>' % (i,i)
            page_html_list.append(temp)

        if self.current_page >= self.all_pager:
            next_page = '<li class="disabled"><a href="#">下一页</a></li>'
        else:
            next_page = '<li><a href="?page=%s">下一页</a></li>' % (self.current_page + 1,)
        page_html_list.append(next_page)

        last_page = '<li><a href="?page=%s">尾页</a></li>' % (self.all_pager,)
        page_html_list.append(last_page)
        # 尾部添加标签
        page_html_list.append('''
                                           </nav>
                                           </ul>
                                       ''')
        return ''.join(page_html_list)



http://lihuaxi.xjx100.cn/news/1469329.html

相关文章

使用mysql:5.6和 owncloud 镜像,构建一个个人网盘。

使用mysql:5.6和 owncloud 镜像&#xff0c;构建一个个人网盘。 1.关闭防火墙 2.在docker中拉去镜像&#xff0c;运行mysql容器 最后一行是容器的默认id 用docker创建一个新的数据库mysqldb设置密码123456 3.拉去网盘owncloud镜像 4.使用软连接连接mysql 5.测试

【TypeScript】枚举类型

在 TypeScript 中&#xff0c;枚举&#xff08;Enum&#xff09;是一种用于定义命名常量集合的数据类型。枚举使代码更加可读和可维护&#xff0c;因为它们为一组具有语义的值提供了命名。 以下是 TypeScript 中枚举的基本用法和特点&#xff1a; // 声明一个枚举 enum Direc…

龙讯旷腾PWmat已部署至曙光智算平台

编者荐语&#xff1a; 近期&#xff0c;龙讯旷腾核心产品PWmat已成功部署至曙光智算AC.sugon.com平台&#xff0c;可为用户提供包括分子建模、第一性原理计算、数据可视化等在内的完备的超级计算云服务&#xff0c;让大家能够轻松上手具有完全自主知识产权的大尺度高性能材料计…

springboot后端返回图片,vue前端接收并显示的解决方案

后端图片数据返回 后端通过二进制流的形式&#xff0c;写入response中 controller层 /*** 获取签到二维码*/GetMapping("/sign-up-pict")public void signUpPict(Long id, Long semId, HttpServletResponse response) throws NoSuchAlgorithmException {signUpServ…

Java动态代理、反射

文章目录 动态代理调用者--->代理--->对象为什么需要代理代理的详细实现过程代码详情 反射反射概念反射中常用的方法所有代码 动态代理 调用者—>代理—>对象 动态代理就是无侵入式的给代码增加新的功能&#xff0c;通过接口保证后面的对象和代理需要实现同一个接…

苹果电脑怎么录屏?步骤详解,看到就是赚到

苹果电脑作为一款受欢迎的高性能设备&#xff0c;不仅在日常工作中发挥着重要作用&#xff0c;还可以用于创造内容&#xff0c;如录制屏幕内容。录屏功能能够帮助用户将屏幕上的活动记录成视频&#xff0c;方便分享、演示或存档。可是您知道苹果电脑怎么录屏吗&#xff1f;通过…

浙大数据结构第八周之08-图8 How Long Does It Take

前置知识&#xff1a; 拓扑排序&#xff1a; /* 邻接表存储 - 拓扑排序算法 */bool TopSort( LGraph Graph, Vertex TopOrder[] ) { /* 对Graph进行拓扑排序, TopOrder[]顺序存储排序后的顶点下标 */int Indegree[MaxVertexNum], cnt;Vertex V;PtrToAdjVNode W;Queue Q Cre…

基于java医院门诊管理系统设计与实现

摘 要 在当今的中国新经济体制下&#xff0c;我国的经济水平发展迅速。并且随着我国经济的快速发展&#xff0c;信息化也得到了充分的发展。在这个信息的的潮流之下&#xff0c;随着计算机性能的不断的提高&#xff0c;计算机从之前的一个只有政府或是科研事业才能触及到的信息…