14. Django博客项目3 点赞点踩&根评论子评论

17.模板页面

文章展示的页面布局和左侧栏使用的个人站点样式, 右侧展示文章详细内容.

0. 新建base.html模板页面
1. 将个人站点页面代码复制到模板页面base.html中
2. 设置替换的区域
3. 个人站点页面继承模板页面

17.1 模板页面

base.html页面代码 ↓
DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>个人站点title>

    {% load static %}

    <script src="{% static 'js/jquery-3.6.0.min.js' %}">script>

    <link rel="stylesheet" href="{% static 'bootstrap-3.3.7-dist/css/bootstrap.min.css' %}">

    <script src="{% static 'bootstrap-3.3.7-dist/js/bootstrap.min.js' %}">script>

    <link rel="stylesheet" href="{% static 'bootstrap-sweetalert-master/dist/sweetalert.css' %}">

    <script src="{% static 'bootstrap-sweetalert-master/dist/sweetalert.min.js' %}">script>

    <link rel="stylesheet" href="/media/css/{{ blog_obj.site_theme }}/">
head>
<body>

<nav class="navbar navbar-inverse">
    <div class="container-fluid">

        <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 navigationspan>
                <span class="icon-bar">span>
                <span class="icon-bar">span>
                <span class="icon-bar">span>
            button>

            <a class="navbar-brand" href="#">{{ request.user.username }}a>
        div>

        <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="#">Actiona>li>
                        <li><a href="#">Another actiona>li>
                        <li><a href="#">Something else herea>li>
                        <li role="separator" class="divider">li>
                        <li><a href="#">Separated linka>li>
                        <li role="separator" class="divider">li>
                        <li><a href="#">One more separated linka>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">Submitbutton>
            form>

            <ul class="nav navbar-nav navbar-right">

                {% if request.user.is_authenticated %}

                    <li><a href="#">{{ request.user.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>
                            <li role="separator" class="divider">li>

                            <li><a href="/logout/">退出登入a>li>
                        ul>
                    li>
                {% else %}

                    <li><a href="/register/">注册a>li>

                    <li><a href="/login/">登入a>li>
                {% endif %}
            ul>
        div>
    div>
nav>

<div class="container-fluid">
    <div class="row">

        <div class="col-md-3">

            <div class="panel panel-primary">
                <div class="panel-heading">分类div>
                <div class="panel-body">
                    {% for sort_obj in sort_list %}
                        <p>
                            <a href="/{{ site_name }}/sort/{{sort_obj.sort__pk  }}">
                                {{ sort_obj.sort__name }}({{ sort_obj.count_num }})
                            a>
                        p>
                    {% endfor %}

                div>
            div>

            <div class="panel panel-info">
                <div class="panel-heading">标签div>
                <div class="panel-body">
                    {% for tag_obj in tag_list %}
                        <p>
                            <a href="/{{ site_name }}/tag/{{ tag_obj.tag__pk }}/">
                                {{ tag_obj.tag__name }}({{ tag_obj.count_num }})
                            a>
                        p>
                    {% endfor %}

                div>
            div>

            <div class="panel panel-default">
                <div class="panel-heading">时间归档div>
                <div class="panel-body">
                    {% for year_month_obj in year_month_list %}

                        <p>{{ year_month_obj.month|date:"Y年m月" }}({{ year_month_obj.count_num }})a>p>
                    {% endfor %}
                div>
            div>

        div>

        {% block centent %}

        {% endblock %}

    div>
div>
body>
html>

17.2 继承模板页面

个人站点继承模板页面
{% extends 'base.html' %}
{% block centent %}

    <div class="col-md-9">

        {% for queryset_obj in  article_query_set %}

            <div class="media">

                <h4 class="media-heading"><a href="">{{ queryset_obj.title }}a>h4>
                <div class="media-left media-middle">
                    <a href="#">
                        <img class="media-object" src="/media/{{ queryset_obj.blog.userinfo.avatar }}" alt="..."
                             width="80">
                    a>
                div>
                <div class="media-body">
                    {{ queryset_obj.desc }}
                div>

                <div class="pull-right">
                    <p>

                        <span>pasted@span>
                        <span><a href="">{{ queryset_obj.blog.userinfo.username }}a>span>

                        <span>{{ queryset_obj.create_time|date:'Y-m-d' }}span>
                        <span class="glyphicon glyphicon-comment">评论({{ queryset_obj.comment_num }})span>
                        <span class="glyphicon glyphicon-thumbs-up">点赞({{ queryset_obj.up_num }})span>
                        <span class="glyphicon glyphicon-thumbs-down">点踩({{ queryset_obj.down_num }})span>
                        <span><a href="">编辑a>span>
                    p>
                div>

                <hr>
            div>
            <nav aria-label="Page navigation">nav>
        {% endfor %}
        {# 利用自动分页器 #}
        {{ page_obj.page_html|safe }}

    div>
{% endblock %}

18. 文章详细展示

在点击文件的标题之后, 展示文章的详细内容.

url的设计格式
127.0.0.1:8000/用户/article/文章的主键

18.1 路由层


def article_desc(request, site_name, article_id):

    article_obj = models.Article.objects.filter(pk=article_id).first()

    if not article_obj:

        return render(request, 'error.html')

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

18.2 文章展示页面

文章展示页面继承 base.html 模板页面.


def article_desc(request, site_name, article_id):

    article_obj = models.Article.objects.filter(pk=article_id).first()

    blog_obj = models.Blog.objects.filter(site_name=site_name).first()

    if not article_obj:

        return render(request, 'error.html')

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

14. Django博客项目3 点赞点踩&根评论子评论

18.3 绑定文章id

主页 与 个人站点 的页面中点击文章标题展示文章的内容, 为文章标题的a标签设置路由.


<a href="/{{ queryset_obj.blog.site_name }}/article/{{ queryset_obj.pk }}/">
    {{ queryset_obj.title }}
a>

14. Django博客项目3 点赞点踩&根评论子评论

<h4 class="media-heading"><a href="/{{ site_name }}/article/{{ queryset_obj.pk }}/">{{ queryset_obj.title }}a>h4>

14. Django博客项目3 点赞点踩&根评论子评论

14. Django博客项目3 点赞点踩&根评论子评论

19. 制作包含标签

文章详细左侧表单需要数据才能展示出来.

获取数据的方法:
1. 可以将个人站点的分类归档代码复制一份. (不推荐)
2. 将左侧栏制作成inclusion_tag包含标签, base模板页面调用inclusion_tag包含标签即可.

制作inclusion_tag包含标签步骤:
1. 在app应用下创建一个templatetags目录
2. 在该文件内创建一个menu.py文件(命名无特殊要求)
3. 在tempales中创建一个left_menu.html文件, 包含标签生成页面的文件.

4. 将需要数据的三个表单html代码复制到 left_menu.html文件中.

3. 在py文件中写上固定代码导入需要的模块, 写业务逻辑, 最后将数据传递给指定的页面.

19.1 自定义模板语法

将个人站点的归档代码复制到menu.py文件中, 导入需要的模块.

将个人站点归档代码可以删除掉.

主表.objects.条件.annotate(别名=聚合函数('从表的字段')).valeu(需要展示的字段)
Count 操作的字段针对的是从表的主键, 主键有唯一标识, 只要涉及统计什么的数量就直接使用 '从表__id'.

Max, Min, Sum, Avg 操作它的字段.


from django import template

register = template.Library()

from app01 import models

from django.db.models import Max, Min, Sum, Count, Avg

from django.db.models.functions import TruncMonth

@register.inclusion_tag('left_menu.html')
def left_menu(site_name):

    blog_obj = models.Blog.objects.filter(site_name=site_name).first()

    if blog_obj:

        article_query_set = models.Article.objects.filter(blog=blog_obj)

        sort_list = models.Sort.objects.filter(blog=blog_obj).annotate(
            count_num=Count('article__pk')).values('pk', 'name', 'count_num')
        print(article_query_set)
        print(sort_list)

        tag_list = models.Tag.objects.filter(blog=blog_obj).annotate(
            count_num=Count('article__pk')).values('pk', 'name', 'count_num')

        year_month_list = models.Article.objects.filter(blog=blog_obj).annotate(month=TruncMonth('create_time')).values(
            'month').annotate(count_num=Count('pk')).values('month', 'count_num')

        return locals()

19.2 获取数据生成模板

将左侧栏表单复制到这里left_menu.html页面中
menu.py生成的数据会被传递到这个页面中, 并渲染出信息.


<link rel="stylesheet" href="/media/css/{{ blog_obj.site_theme }}/">

<div class="panel panel-primary">
    <div class="panel-heading">分类div>
    <div class="panel-body">
        {% for sort_obj in sort_list %}
            <p>
                <a href="/{{ site_name }}/sort/{{sort_obj.pk  }}">
                    {{ sort_obj.name }}({{ sort_obj.count_num }})
                a>
            p>
        {% endfor %}

    div>
div>

<div class="panel panel-info">
    <div class="panel-heading">标签div>
    <div class="panel-body">
        {% for tag_obj in tag_list %}
            <p>
                <a href="/{{ site_name }}/tag/{{ tag_obj.pk }}/">
                    {{ tag_obj.name }}({{ tag_obj.count_num }})
                a>
            p>
        {% endfor %}

    div>
div>

<div class="panel panel-default">
    <div class="panel-heading">时间归档div>
    <div class="panel-body">
        {% for year_month_obj in year_month_list %}

            <p>{{ year_month_obj.month|date:"Y年m月" }}({{ year_month_obj.count_num }})a>p>
        {% endfor %}
    div>
div>

19.3 使用包含标签

在模板页面base.html 中调用包含标签.


{% load menu %}

{% left_menu site_name %}

14. Django博客项目3 点赞点踩&根评论子评论

19.4 文章展示问题

现在文章就是一个段落p标签展示, 没有任何的样式, 如果想让文章变的好看就因该使用html来写.

使用Typora写好之后到处HTML格式文件.

14. Django博客项目3 点赞点踩&根评论子评论
打开导出的文件, 开发开发者工具复制html写的博客代码.

14. Django博客项目3 点赞点踩&根评论子评论
进入后台管理, 将HTML代码文章写入数据库中.

14. Django博客项目3 点赞点踩&根评论子评论
文章页面中 使用safe过滤器将渲染成HTML页面.

<p>{{ article_obj.content|safe }}p>
复制博客园的文章 复制div id="cnblogs_post_body" 的标签.

14. Django博客项目3 点赞点踩&根评论子评论

14. Django博客项目3 点赞点踩&根评论子评论
将所有的文章内容都替换成html格式的文章, 图片有防盗技术就无法展示.

14. Django博客项目3 点赞点踩&根评论子评论

20. 文章点赞点踩

2.1 点赞点踩按钮

复制博客园的点赞, 点赞的HTML代码.

14. Django博客项目3 点赞点踩&根评论子评论
1. 复制HTML标签
<div id="div_digg">
    <div class="diggit" onclick="votePost(16081428,'Digg')">
        <span class="diggnum" id="digg_count">0span>
    div>
    <div class="buryit" onclick="votePost(16081428,'Bury')">
        <span class="burynum" id="bury_count">0span>
    div>
    <div class="clear">div>
    <div class="diggword" id="digg_tips">
    div>
div>

14. Django博客项目3 点赞点踩&根评论子评论
2. 将点击事件删除掉后, 复制到article.html页面中
3. 复制  div_digg diggit diggnum buryit burynum clear diggword 的样式, 一个一个复制下来.

14. Django博客项目3 点赞点踩&根评论子评论
#div_digg {
    float: right;
    margin-bottom: 10px;
    margin-right: 30px;
    font-size: 12px;
    width: 125px;
    text-align: center;
    margin-top: 10px;
}

.diggit {
    float: left;
    width: 46px;
    height: 52px;
    background: url(//static.cnblogs.com/images/upup.gif) no-repeat;
    text-align: center;
    cursor: pointer;
    margin-top: 2px;
    padding-top: 5px;
}
.diggnum {
    font-size: 14px;
    color: #075db3;
}

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

.burynum {
    font-size: 14px;
    color: #075db3;
}

.clear {
    clear: both;
}

.diggword {
    margin-top: 5px;
    margin-left: 0;
    font-size: 12px;
    color: #808080;
}
4. 博客园的图片有防盗技术, 将图片下载到本地.

   https://static.cnblogs.com/images/upup.gif
   https://static.cnblogs.com/images/downdown.gif

14. Django博客项目3 点赞点踩&根评论子评论

14. Django博客项目3 点赞点踩&根评论子评论

14. Django博客项目3 点赞点踩&根评论子评论

background: url(/static/img/upup.jpg) no-repeat;
background: url(/static/img/downdown.jpg) no-repeat;
5. 在base.html的<heah>标签中设置css的替换区域.


{% block css %}
{% endblock %}
{% extends 'base.html' %}
{% block css %}
<style>
    #div_digg {
        float: right;
        margin-bottom: 10px;
        margin-right: 30px;
        font-size: 12px;
        width: 125px;
        text-align: center;
        margin-top: 10px;
    }

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

    .diggnum {
        font-size: 14px;
        color: #075db3;
    }

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

    .burynum {
        font-size: 14px;
        color: #075db3;
    }

    .clear {
        clear: both;
    }

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

{% block centent %}
<div class="col-md-9">

    <div>
        <h1 class="text-center">{{ article_obj.title }}h1>
        <p>{{ article_obj.content|safe }}p>
    div>

    <div>
        <div id="div_digg">

            <div class="diggit action ">
                <span class="diggnum" id="digg_count">{{ article_obj.up_num }}span>
            div>

            <div class="buryit action">
                <span class="burynum" id="bury_count">{{ article_obj.down_num }}span>
            div>

            <div class="clear">div>
            <div class="diggword" id="digg_tips"><span id="msg" style="color: red">span>div>

        div>
    div>
div>

{% endblock %}

14. Django博客项目3 点赞点踩&根评论子评论

<div class="clear">div>

14. Django博客项目3 点赞点踩&根评论子评论

14. Django博客项目3 点赞点踩&根评论子评论

2.2 绑定事件

在base.html设置js独立的区域.

{% block js %}
    <script>

        $(".action").on('click', function () {

        let is_Up = $(this).hasClass('diggit')

        $.ajax({

            url: '/up_or_down/',

            type: 'post',

            data: {

                "csrfmiddlewaretoken": '{{ csrf_token }}',

                'article_id': '{{ article_obj.pk }}',

                is_Up: is_Up
            },

            success: function (args) {

                if (args.code === 200) {

                    $('#msg').text(args.right_msg)

                    $('#digg_count').text(args.up_num)
                    $('#bury_count').text(args.down_num)
                }else{

                    $('#msg').html(args.error_msg)
                }

            }
        })
    })

 </script>

 {% endblock %}

2.3 获取ajax数据

1. 判断请求方式
2. 判断用户是否登入, (文章可以是不登入的用户看, 点赞点踩则需要登入)
3. 判断用户是否给自己的文章点赞
4. 判断用户是否点击过这篇文件
5. 判断点赞还是点踩
逻辑有点先, 先写正确的逻辑在写错误的逻辑.

操作两张表:
Article 中使用F查询, 多点击的数据进行操作
UpAndDown 表中记录 那个用户给哪篇文章点赞还是点踩.

user_id  article_id is_up (True为点赞/ False为点踩)
  1          3        True
判断某个用户时候给某篇文章是否点击过. 点赞点踩表对象.filter (user_id and article_id) 满足这两个条件
说明点过了.


def up_or_down(request):

    if request.is_ajax():

        back_dict = {'code': 200}

        if request.user.is_authenticated():

            post_obj = request.POST
            article_id = post_obj.get('article_id')
            is_up = post_obj.get('is_Up')

            import json
            is_up = json.loads(is_up)

            article_obj = models.Article.objects.filter(pk=article_id).first()

            if article_obj.blog.userinfo.username != request.user.username:

                up_or_down_obj = models.UpAndDown.objects.filter(user_id=request.user.pk, article_id=article_obj)

                if not up_or_down_obj:

                    if is_up:

                        models.Article.objects.filter(pk=article_id).update(up_num=F('up_num') + 1)
                        back_dict['right_msg'] = '点赞成功!'

                    else:
                        models.Article.objects.filter(pk=article_id).update(down_num=F('down_num') - 1)
                        back_dict['right_msg'] = '点踩成功!'

                    models.UpAndDown.objects.create(user=request.user, article=article_obj, is_up=is_up)
                    article_obj = models.Article.objects.filter(pk=article_id).first()
                    back_dict['up_num'] = article_obj.up_num
                    back_dict['down_num'] = article_obj.down_num

                else:

                    back_dict = {'code': 401, 'error_msg': '已经评论过不能在评论!'}

            else:

                back_dict = {'code': 402, 'error_msg': '自己的文章此功能不可操作!'}

        else:

            back_dict = {'code': 403, 'error_msg': '请先登入账户!'}

        print(back_dict)
        return JsonResponse(back_dict)

    else:
        return HttpResponse('非法访问')

is_up = request.POST.get('is_Up')

import json
is_up = json.loads(is_up)

点击别人的文章

14. Django博客项目3 点赞点踩&根评论子评论
点击自己的文章

14. Django博客项目3 点赞点踩&根评论子评论
没有登入时点击
back_dict = {'code': 403, 'error_msg': '请先登入账户!'}

14. Django博客项目3 点赞点踩&根评论子评论

2.4 点赞点踩计数

两种方法:
1. 后端写入数据之后, 将文章对象发给前端刷新计数.

2. 后端获取包含计数标签的值, 做加减计算, 再刷新显示.

采用方式1, 在后端已经计算过, 就没必要在后端计算了.

back_dict['up_num'] = article_obj.up_num
back_dict['down_num'] = article_obj.down_num
$('#digg_count').text(args.up_num)
$('#bury_count').text(args.down_num)

14. Django博客项目3 点赞点踩&根评论子评论

21. 评论

21.1 根评论

在文章展示页面article.html部署一个评论框.


<div>
    <p><span class="glyphicon glyphicon-comment">发表评论span>p>
    <textarea name="comment" id="id_comment" cols="45" rows="10">textarea>
    <p>
        <button id="id_btn">提交button>
        <span id="id_error">span>
    p>
div>

14. Django博客项目3 点赞点踩&根评论子评论
设置浮动让评论和点赞点赞不在同一行当中.


<div class="clearfix">
    <div id="div_digg">
    ...

 div>div>

14. Django博客项目3 点赞点踩&根评论子评论

21.2 提交评论

ajax提交评论的信息, 那个用户给哪篇文章评论了什么.


$('#id_btn').on('click', function () {

    let comment = $("[name='comment']").val()

    $.ajax({

        url: '/submit_comment/',

        type: 'post',

        data: {

            article_id: '{{ article_obj.pk }}',

            comment: comment,

            "csrfmiddlewaretoken": '{{ csrf_token }}',
        },
        success: function (args) {
            alert(args)
        }
    })
})

21.3 将提交信息写入表中

先写正确的逻辑.


def submit_comment(request):

    if request.is_ajax():

        if request.user.is_authenticated():

            post_obj = request.POST
            article_id = post_obj.get('article_id')
            comment = post_obj.get('comment')
            print(article_id, comment)

            models.Article.objects.filter(pk=article_id).update(comment_num=F('comment_num') + 1)

            article_obj = models.Article.objects.filter(pk=article_id).first()

            models.Comment.objects.create(user=request.user, article=article_obj, content=comment)

            back_dict = {'code': 200, 'right_msg': '评论成功'}

            else:
                back_dict = {'code': '400', 'error_msg': '请先登入在评论'}

        return JsonResponse(back_dict)

    else:
        return HttpResponse('非法访问')

14. Django博客项目3 点赞点踩&根评论子评论

21.4 展示根评论

文章详细展示中, 获取评论的内容, 并渲染到前端.


comment_obj = models.Comment.objects.filter(article_id=article_id)

<div>

    {% for obj in comment_obj %}

    <div class="panel panel-default">

        <div class="panel-heading">
            <p>
                <span>{{ forloop.counter }}楼span>
                <span>{{ obj.comment_time|date:'Y-m-d h:i:s' }}span>

                <span>{{ obj.user.username }}span>
                <span class="pull-right"><a href="#">回复a>span>
            p>
        div>

        <div class="panel-body">

            <div>
                {{ obj.content }}
            div>
        div>
    div>
    {% endfor %}
div>

14. Django博客项目3 点赞点踩&根评论子评论

21.5 提交评论时渲染

1. 点击提交评论按键后清空评论框内的内容,
2. 渲染自己的评论
   方式1: DOM临时渲染
   方式2: 页面刷新render渲染
模板代码${替换的内容}...
success:function (args) {

    if (args.code === 200) {

        $('#id_comment').val('')

        let username = '{{ request.user.username }}'

        let tem = 

                         ${username}

                ${comment}

        $('#id_exhibit').append(tem)
    }
}else{

}

14. Django博客项目3 点赞点踩&根评论子评论
没有用户登入时渲染提示登入信息.

if (args.code === 200) {
    ...

}else{

    $('#id_error').html(args.error_msg)

14. Django博客项目3 点赞点踩&根评论子评论

21.6 子评论

点击回复书写字评论.

点击回复的时候, 鼠标聚焦到评论框中.

格式:
@用户名(换行)
|(<--光标在这)
* 为回复按钮绑定点击事件, 这个回复是在for循环内不能使用id值来标记, 可以使用类来标记. 之后查找该标签.

需要获取的信息, 评论用户的名字, 根评论的主键id, 可以在for循环中为标签自定义两个属性接收需要的值.

注意点 a标签不要有href, href='#'会跳转到页面头部.


<span class="pull-right"><a class="reply" username="{{ obj.user.username }}" parent_id="{{ obj.pk }}">回复a>span>

14. Django博客项目3 点赞点踩&根评论子评论

$('.reply').on('click', function (){

    let username = $(this).attr('username')

    let comment_id = $(this).attr('parent_id')

    $('#id_comment').val('@' + username + '\n').focus()
})

14. Django博客项目3 点赞点踩&根评论子评论
自评与根评论都是同一个按钮提交数据.

区分根评论和子评论: 子评论的parent_id有值. parent_id指向评论表中的id.

14. Django博客项目3 点赞点踩&根评论子评论
{% block js %}
    <script>

        $(".action").on('click', function () {

        let is_Up = $(this).hasClass('diggit')

        $.ajax({

            url: '/up_or_down/',

            type: 'post',

            data: {

                "csrfmiddlewaretoken": '{{ csrf_token }}',

                'article_id': '{{ article_obj.pk }}',

                'is_Up': is_Up
            },

            success: function (args) {

                if (args.code === 200) {

                    $('#msg').text(args.right_msg)

                    $('#digg_count').text(args.up_num)
                    $('#bury_count').text(args.down_num)
                } else {

                    $('#msg').html(args.error_msg)
                }

            }
        })
    })

 var parent_id = null

 $('#id_btn').on('click', function () {

     let comment = $("[name='comment']").val()

     $.ajax({

         url: '/submit_comment/',

         type: 'post',

         data: {

             'article_id': '{{ article_obj.pk }}',

             'comment': comment,

             'parent_id': parent_id,

             "csrfmiddlewaretoken": '{{ csrf_token }}',
         },
         success: function (args) {

             if (args.code === 200) {

                 $('#id_comment').val('')

                 let username = '{{ request.user.username }}'

                 let tem = 

                                ${username}

                        ${comment}

                 $('#id_exhibit').append(tem)
             }
         }
     })
 })

 $('.reply').on('click', function () {

     let username = $(this).attr('username')

     parent_id = $(this).attr('parent_id')

     $('#id_comment').val('@' + username + '\n').focus()
 })
 </script>

14. Django博客项目3 点赞点踩&根评论子评论

def submit_comment(request):

    if request.is_ajax():

        if request.user.is_authenticated():

            post_obj = request.POST
            article_id = post_obj.get('article_id')
            comment = post_obj.get('comment')
            parent_id = post_obj.get('parent_id')
            print(article_id, comment)

            models.Article.objects.filter(pk=article_id).update(comment_num=F('comment_num') + 1)

            article_obj = models.Article.objects.filter(pk=article_id).first()

            models.Comment.objects.create(user=request.user, article=article_obj, content=comment, parent_id=parent_id)

            back_dict = {'code': 200, 'right_msg': '评论成功'}

        else:
            back_dict = {'code': '400', 'error_msg': '请先登入在评论'}

        return JsonResponse(back_dict)

    else:
        return HttpResponse('非法访问')

14. Django博客项目3 点赞点踩&根评论子评论

print(parent_id, type(parent_id))

14. Django博客项目3 点赞点踩&根评论子评论

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4pevzSlH-1649082690806)(https://s2.loli.net/2022/04/02/Emg7GxoijZlkR15.gif)]

14. Django博客项目3 点赞点踩&根评论子评论
子评论中@用户 不是评论的内容不因该写进数据中, 将这部分进行切除.

14. Django博客项目3 点赞点踩&根评论子评论

$('#id_btn').on('click', function () {

    let comment = $("[name='comment']").val()

    if (parent_id) {

        let IndexNum = comment.indexOf('\n') + 1

        comment = comment.slice(IndexNum)
    }
    ...

14. Django博客项目3 点赞点踩&根评论子评论

21.7 子评论的渲染

格式:
@用户
信息
<div class="panel-body">

    <div>

        {% if obj.parent_id %}
        <div>

            <p><span style="color: #aaff00">@{{ obj.parent.user.username  }}span>p>

        div>
        {% endif %}
        {{ obj.content }}
    div>
div>
obj.parent.user.username
评论的数据对象.自关联的外键, 拿到根评论的数据对象.外键.username拿到根评论的用户名.

21.8 重置子评论全局变量

第一次写是子评论
第二次写是根评论

14. Django博客项目3 点赞点踩&根评论子评论
这时会发现都是子评论, 因为parent_id获取到值之后就一直有值,
不重置为null的话, 获取到了值, 之后永远都是子评论.

14. Django博客项目3 点赞点踩&根评论子评论
在回调函数的末尾重置parent_id为null

14. Django博客项目3 点赞点踩&根评论子评论
再次测试, 先写一个子评论, 在写一个根评论.

14. Django博客项目3 点赞点踩&根评论子评论
正常

14. Django博客项目3 点赞点踩&根评论子评论
文章展示代码
{% extends 'base.html' %}
{% block css %}
    <style>
        #div_digg {
            float: right;
            margin-bottom: 10px;
            margin-right: 30px;
            font-size: 12px;
            width: 125px;
            text-align: center;
            margin-top: 10px;
        }

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

        .diggnum {
            font-size: 14px;
            color: #075db3;
        }

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

        .burynum {
            font-size: 14px;
            color: #075db3;
        }

        .clear {
            clear: both;
        }

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

{% block centent %}

    <div class="col-md-9">

        <div>
            <h1 class="text-center">{{ article_obj.title }}h1>
            <p>{{ article_obj.content|safe }}p>
        div>

        <div class="clearfix">
            <div id="div_digg">

                <div class="diggit action ">
                    <span class="diggnum" id="digg_count">{{ article_obj.up_num }}span>
                div>

                <div class="buryit action">
                    <span class="burynum" id="bury_count">{{ article_obj.down_num }}span>
                div>

                <div class="clear">div>
                <div class="diggword" id="digg_tips"><span id="msg" style="color: red">span>div>

            div>
        div>

        <div id="id_exhibit">

            {% for obj in comment_obj %}
                <div class="panel panel-default">

                    <div class="panel-heading">
                        <p>
                            <span>{{ forloop.counter }}楼span>
                            <span>{{ obj.comment_time|date:'Y-m-d h:i:s' }}span>

                            <span>{{ obj.user.username }}span>

                            <span class="pull-right"><a class="reply" username="{{ obj.user.username }}"
                                                        parent_id="{{ obj.pk }}">回复a>span>
                        p>
                    div>

                    <div class="panel-body">

                        <div>

                            {% if obj.parent_id %}
                                <div>

                                    <p><span style="color: #aaff00">@{{ obj.parent.user.username }}span>p>

                                div>
                            {% endif %}
                            {{ obj.content }}
                        div>
                    div>
                div>
            {% endfor %}
        div>

        <div>
            <p><span class="glyphicon glyphicon-comment">发表评论span>p>
            <textarea name="comment" id="id_comment" cols="45" rows="10">textarea>
            <p>
                <button id="id_btn">提交button>
                <span id="id_error">span>
            p>
        div>
    div>

{% endblock %}

{% block js %}
    <script>

        $(".action").on('click', function () {

            let is_Up = $(this).hasClass('diggit')

            $.ajax({

                url: '/up_or_down/',

                type: 'post',

                data: {

                    "csrfmiddlewaretoken": '{{ csrf_token }}',

                    'article_id': '{{ article_obj.pk }}',

                    'is_Up': is_Up
                },

                success: function (args) {

                    if (args.code === 200) {

                        $('#msg').text(args.right_msg)

                        $('#digg_count').text(args.up_num)
                        $('#bury_count').text(args.down_num)
                    } else {

                        $('#msg').html(args.error_msg)
                    }

                }
            })
        })

        var parent_id = null

        $('#id_btn').on('click', function () {

            let comment = $("[name='comment']").val()

            if (parent_id) {

                let IndexNum = comment.indexOf('\n') + 1

                comment = comment.slice(IndexNum)
            }

            $.ajax({

                url: '/submit_comment/',

                type: 'post',

                data: {

                    'article_id': '{{ article_obj.pk }}',

                    'comment': comment,

                    'parent_id': parent_id,

                    "csrfmiddlewaretoken": '{{ csrf_token }}',
                },
                success: function (args) {

                    if (args.code === 200) {

                        $('#id_comment').val('')

                        let username = '{{ request.user.username }}'

                        let tem = 

                                        ${username}

                                ${comment}

                        $('#id_exhibit').append(tem)

                    }else{

                        $('#id_error').html(args.error_msg)

                    }

                    parent_id = null
                }
            })
        })

        $('.reply').on('click', function () {

            let username = $(this).attr('username')

            parent_id = $(this).attr('parent_id')

            $('#id_comment').val('@' + username + '\n').focus()

        })
    script>

{% endblock %}

Original: https://blog.csdn.net/qq_46137324/article/details/123888270
Author: 开局签到Python基础
Title: 14. Django博客项目3 点赞点踩&根评论子评论

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/735716/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • 8行代码实现快速排序,简单易懂图解!

    快速排序是一种常用的排序算法,比选择排序快的多。在之前的我随笔中也写过关于快速排序的算法,也可以看一下和现在的区别python实现快速排序 – Mr-Yang – 博客…

    Python 2023年5月23日
    051
  • MySQL存储引擎详解(一)-InnoDB架构

    目录 前言 一、支持的存储引擎 二、InnoDB引擎 1.Buffer Pool 传统LUR算法 预读 预读失效 2.Log Buffer 3.Adaptive Hash Inde…

    Python 2023年9月15日
    030
  • Python游戏开发,pyqt5模块,Python实现AI的俄罗斯方块小游戏,看你能过几关?

    前言 利用一个简单的算法制作带AI的俄罗斯方块小游戏,让我们愉快地开始吧~ ; 代码效果展示 开发工具 Python版本: 3.6.4 相关模块: pyqt5模块; 以及一些Pyt…

    Python 2023年9月20日
    052
  • Transformer学习资源&顺序推荐

    因为我个人不喜欢听一个老师重复讲而喜欢听多位老师讲同一个东西所以整理了一下这份清单,我觉得比我自己的学习顺序要好一些! 因为我个人不喜欢听一个老师重复讲而喜欢听多位老师讲同一个东西…

    Python 2023年10月28日
    033
  • pytest学习总结

    pytest需要学习的内容包含以下:1、pytest入门2、pytest高阶用法fixture3、pytest高阶用法之处理不能成功的测试用例4、pytest高阶用法之函数数据参数…

    Python 2023年9月14日
    034
  • VM虚拟机centos 7下配置静态IP上网

    1、默认的网络配置是使用dhcp方式分配ip地址,这种方式会在系统每次联网的时候分配一个ip给我们用,也就是说有可 能系统下次启动的时候ip会变,这样非常不方便我们管理。 2、 本…

    Python 2023年6月12日
    054
  • conda环境迁移

    conda环境迁移 需求:本机环境迁移到服务器,方便在服务器上训练、测试大批量的数据。 方法一: 参考:conda环境从一个服务器迁移到另一个服务器 1. 安装打包工具 conda…

    Python 2023年9月7日
    099
  • MySQL经典案例50题

    建表、插入数据 CREATE TABLE Student( s_id VARCHAR(20), s_name VARCHAR(20) NOT NULL DEFAULT”, s_b…

    Python 2023年10月24日
    046
  • Java描述 LeetCode,131. 分割回文串

    𝑰’𝒎 𝒉𝒉𝒈, 𝑰 𝒂𝒎 𝒂 𝒈𝒓𝒂𝒅𝒖𝒂𝒕𝒆 𝒔𝒕𝒖𝒅𝒆𝒏𝒕 𝒇𝒓𝒐𝒎 𝑵𝒂𝒏𝒋𝒊𝒏𝒈, 𝑪𝒉𝒊𝒏𝒂. 🏫 𝑺𝒉𝒄𝒐𝒐𝒍: 𝑯𝒐𝒉𝒂𝒊 𝑼𝒏𝒊𝒗𝒆𝒓𝒔𝒊𝒕𝒚 🌱 𝑳…

    Python 2023年10月24日
    059
  • 【JS 逆向百例】某公共资源交易网,公告 URL 参数逆向分析

    声明 本文章中所有内容仅供学习交流,抓包内容、敏感网址、数据接口均已做脱敏处理,严禁用于商业用途和非法用途,否则由此产生的一切后果均与作者无关,若有侵权,请联系我立即删除! 逆向目…

    Python 2023年5月23日
    073
  • OpenAI的通用人工智能神器ChatGPT简介

    啊哦~你想找的内容离你而去了哦 内容不存在,可能为如下原因导致: ① 内容还在审核中 ② 内容以前存在,但是由于不符合新 的规定而被删除 ③ 内容地址错误 ④ 作者删除了内容。 可…

    Python 2023年11月4日
    030
  • python xlim_在matplotlib中设置时间序列的xlim

    我想在matplotlib中画一个图,显示2016年和2017年8月的温度。x轴是时间,y轴是温度。我尝试通过共享x轴(从2016-08-01 00:00:00到2016-08-3…

    Python 2023年9月5日
    046
  • Flask学习_07_Cookie 与 Session

    Cookie 与 Session 设置 set_cookie 实质:在响应头中增加了一个set_cookie响应头。读取 request.cookies.get 。请求体里有了一个…

    Python 2023年8月12日
    048
  • python-django自定义分页

    1、数据库测试表 +—–+———+—–+| uid | name | age |+—…

    Python 2023年8月6日
    035
  • Pandas文本数据

    一、合并功能 (一)merge:pd.merge() 类似于vlookup 函数的作用,只会返回两个表中都含有的元素。表达形式 pd.merge(left,right,how = …

    Python 2023年8月22日
    035
  • 【语音算法】wav2vec系列原理和使用

    文章目录 前言 1. wav2vec 2. vq-wav2vec 3. wav2vec2.0 * 3.1 encoder 3.2 context 3.3 wav2vec2.0的使用…

    Python 2023年10月26日
    039
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球