7 功能5:文章详情页、评论、评论树

1、文章详情页之评论如何实现?

 2、根评论

1、根评论样式

  

        input.author {
            background-image: url('/static/img/icon_form.gif');
            background-repeat: no-repeat;
            border: 1px solid #ccc;
            padding: 4px 4px 4px 30px;
             230px;
        }
              <div class="comments">
                    <p>发表评论</p>
                    <p>昵称</p><input type="text" id="tbCommentAuthor" class="author" disabled="disabled" size="50"
                                    value={{ request.user.username }}>
                    <p>评论内容</p>
                    <textarea name="" id="" cols="60" rows="10"></textarea>
                    <p>
                        <button class="btn btn-default comment_btn">提交评论</button>
                    </p>
                </div>

 

2、提交根评论

 

 

 

 3、显示根评论

                    <p>评论列表</p>
                    <ul class="list-group comment_list">
                        {% for comment in comment_list %}
                            <li class="list-group-item">
                                <div class="comment_head">
                                    <a href=""># {{ forloop.counter }}楼</a>&nbsp;&nbsp;
                                    <span>{{ comment.create_time|date:"Y-m-d H:i" }}</span>&nbsp;&nbsp;
                                    <a href="">{{ comment.user.username }}</a>
                                    <a href="" class="pull-right">回复</a>
                                </div>

                                <div class="comment_content">
                                    <p>{{ comment.content }}</p>
                                </div>
                            </li>
                        {% endfor %}

                    </ul>
评论列表

 4、ajax显示评论:es6 模板字符串

  • ES6引入了模板字符串解决 字符串拼接问题+++

  深入了解es6 模板字符串 https://www.cnblogs.com/bfc0517/p/6700849.html

def comment(request):
    """评论视图"""
    article_id = request.POST.get("article_id")
    content = request.POST.get("content")
    parent_id = request.POST.get("parent_id")
    user_id = request.user.pk

    # 创建一条评论
    comment_obj = models.Comment.objects.create(user_id=user_id,article_id=article_id,
                                                content=content,parent_comment_id=parent_id)

    response = {}
    response["create_time"] = comment_obj.create_time.strftime("%Y-%m-%d %X")
    response["username"] = request.user.username
    response["content"] = content


    return JsonResponse(response)
根评论视图

   //评论请求
    $('.comment_btn').click(function () {
        var parent_id = ""
        var content = $("#comment_content").val()
        $.ajax({
            url: '/comment/',
            type: "post",
            data: {
                "csrfmiddlewaretoken": $("[name='csrfmiddlewaretoken']").val(),
                "article_id":{{ article_obj.pk }},
                "content": content,
                "parent_id": parent_id

            },
            success: function (data) {
                console.log(data);

                // dom操作  es6  标签字符串
                var create_time = data.create_time
                var username = data.username
                var content = data.content

                var s = `
                        <li class="list-group-item">
                            <div class="comment_head">
                                <span>${create_time}</span>&nbsp;&nbsp;
                                <a href="">${username}</a>
                                <a href="" class="pull-right">回复</a>
                            </div>

                            <div class="comment_content">
                                <p>${content}</p>
                            </div>
                        </li>`;


                $("ul").append(s)

                //清空评论框
                $("#comment_content").val("")
            }
        })
    })
View Code

3、子评论

1、回复按钮

2、提交子评论

如果是子评论,就从
开始取字符串

 

# indexOf()
定义和用法
indexOf() 方法可返回某个指定的字符串值在字符串中首次出现的位置。
语法
stringObject.indexOf(searchvalue,fromindex)

# slice()
定义和用法
slice() 方法可从已有的数组中返回选定的元素。
语法
arrayObject.slice(start,end)

 

3、render显示子评论

 4、ajax显示子评论

if 父评论

  显示s  

如果子评论

  显示 新的样式

4、评论树

1、评论树简介

  1、方式1:递归完成
     2、方式2:评论树展示数据

2、评论树的请求数据

    # 评论
    re_path('^comment/$', views.comment, name='comment'),
    # 评论树
    re_path('^get_comment_tree/$', views.get_comment_tree, name='get_comment_tree'),

def get_comment_tree(request):
    """评论树"""
    article_id = request.GET.get("article_id")
    ret = list(models.Comment.objects.filter(article_id=article_id).values("pk", "content","parent_comment_id"))

    return JsonResponse(ret,safe=False)

     

 3、展开评论树1:根评论

 4、展开评论树2:子评论

 5、评论树思考

    //评论树
    $(".tree_btn").click(function () {
        $.ajax({
            url: "/get_comment_tree/",
            type: "get",
            data: {
                article_id:{{ article_obj.pk }}
            },
            success: function (comment_list) {
                console.log(comment_list)

                $.each(comment_list, function (index, comment_object) {
                    var pk = comment_object.pk
                    var content = comment_object.content
                    var parent_comment_id = comment_object.parent_comment_id

                    var s = '<div class="comment_item" comment_id=' + pk + '><span>' + content + '</span>' + '</div>'

                    if (!parent_comment_id) {
                        //根评论展示
                        $(".comment_tree").append(s)

                    } else {
                        //子评论展示
                        $("[comment_id="+parent_comment_id+ "]").append(s)
                    }

                })
            }
        })
    })

 

 5、评论之事务操作

  事务同步:同进同退

6、邮件发送评论

django中配置邮件 

https://blog.csdn.net/xinxinnogiveup/article/details/78900811

https://code.ziqiangxuetang.com/django/django-send-email.html

1、settings配置

# 发送邮件
EMAIL_USE_SSL = True
# EMIAL_HOST = 'smtp.exmail.qq.com'       # 如果是163 改成smtp.163.com
EMAIL_HOST = 'smtp.qq.com'  # 如果是 163 改成 smtp.163.com
EMAIL_PORT = 465
EMAIL_HOST_USER = '7xxxxxxxx@qq.com'        # 账号
EMAIL_HOST_PASSWORD = 'oXXXXXxxxxx'    # qq邮箱的授权码而不是密码
DEFAULT_FROM_EMAIL = EMAIL_HOST_USER 

qq邮箱授权码如何开启?

 http://service.mail.qq.com/cgi-bin/help?id=28&no=1001256&subtype=1

 

2、from django.core.mail import send_mail

3、多进程实现

 

 6、评论的完整代码

url

    # 个人站点页面设计
    re_path(r'^(?P<username>w+)$', views.home_site, name='home_site'),
    # 个人站点的跳转
    re_path(r'^(?P<username>w+)/(?P<condition>tag|category|archive)/(?P<param>.*)/$', views.home_site, name='home_site'),


    # 文章详情页
    re_path(r'^(?P<username>w+)/articles/(?P<article_id>d+)$', views.article_detail, name='article_detail'),

    # 点赞
    re_path('^digg/$', views.digg, name='digg'),

    # 评论
    re_path('^comment/$', views.comment, name='comment'),
    # 评论树
    re_path('^get_comment_tree/$', views.get_comment_tree, name='get_comment_tree'),
View Code

settings

LANGUAGE_CODE = 'en-us'


# 时区选择
# TIME_ZONE = 'UTC'
TIME_ZONE = 'Asia/Shanghai'

USE_I18N = True

USE_L10N = True

# 转换时区
# USE_TZ = True
USE_TZ = False



# 发送邮件
EMAIL_USE_SSL = True
# EMIAL_HOST = 'smtp.exmail.qq.com'       # 如果是163 改成smtp.163.com
EMAIL_HOST = 'smtp.qq.com'  # 如果是 163 改成 smtp.163.com
EMAIL_PORT = 465
EMAIL_HOST_USER = '7fadfasas0@qq.com'        # 账号
EMAIL_HOST_PASSWORD = 'ord大师傅bdie'    # qq邮箱的授权码而不是密码
DEFAULT_FROM_EMAIL = EMAIL_HOST_USER
View Code

view视图

from django.shortcuts import render, HttpResponse, redirect
from blog.utils.validCode import get_validCode_img  # 导入验证码函数
from django.http import JsonResponse  # Json数据返回到前端
from django.contrib import auth  # 用户认证组件
from blog.models import UserInfo
from blog.myForms import UserForm  # froms组件
from blog import models
from django.db.models import Avg, Max, Min, Count, F, Q
from django.db import transaction  # 事务操作



def article_detail(request, username, article_id):
    """文章详情页"""
    user_obj = models.UserInfo.objects.filter(username=username).first()
    blog = user_obj.blog

    # article_id对应的文章
    article_obj = models.Article.objects.filter(pk=article_id).first()

    # 评论显示
    comment_list = models.Comment.objects.filter(article_id=article_id)

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


import json


def digg(request):
    """点赞视图"""
    # print(request.POST)
    article_id = request.POST.get("article_id")
    # article_id = request.POST.get("is_up")       # true
    is_up = json.loads(request.POST.get("is_up"))  # True
    user_id = request.user.pk  # 点赞人即为登录人

    # 点赞记录以及存在
    from django.http import JsonResponse  # Json数据返回到前端
    response = {"state": True, "msg": None, "handled": None}

    obj = models.ArticleUpDown.objects.filter(user_id=user_id, article_id=article_id).first()
    if not obj:
        # 创建一条点赞记录
        ard = models.ArticleUpDown.objects.create(user_id=user_id, is_up=is_up, article_id=article_id)
        from django.db.models import F
        # 点赞数的保存
        queryset = models.Article.objects.filter(pk=article_id)
        if is_up:
            queryset.update(up_count=F("up_count") + 1)
        else:
            queryset.update(down_count=F("down_count") - 1)
    else:
        response['state'] = False
        response['handled'] = obj.is_up

    return JsonResponse(response)


def comment(request):
    """评论视图"""
    article_id = request.POST.get("article_id")
    content = request.POST.get("content")
    parent_id = request.POST.get("parent_id")
    user_id = request.user.pk

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

    with transaction.atomic():  # 事务操作
        # 创建一条评论
        comment_obj = models.Comment.objects.create(user_id=user_id, article_id=article_id,
                                                    content=content, parent_comment_id=parent_id)
        # yun   # 事务中断
        # 评论 comment_count  +1
        models.Article.objects.filter(pk=article_id).update(comment_count=F("comment_count") + 1)

    # 发送邮件
    from django.core.mail import send_mail
    from cnblog import settings
    """
    send_mail(
        "你的文章【%s】新增了一条评论内容" % article_obj.title,
        content,
        settings.EMAIL_HOST_USER,
        ['849923747@qq.com']

    )
"""

    import threading  # 多进程发送邮件
    t = threading.Thread(target=send_mail, args=("你的文章【%s】新增了一条评论内容" % article_obj.title,
                                                 content,
                                                 settings.EMAIL_HOST_USER,
                                                 ["849923747@qq.com"]
                                                 ))
    t.start()


    response = {}
    response["create_time"] = comment_obj.create_time.strftime("%Y-%m-%d %X")
    response["username"] = request.user.username
    response["content"] = content

    return JsonResponse(response)


def get_comment_tree(request):
    """评论树"""
    article_id = request.GET.get("article_id")
    ret = list(models.Comment.objects.filter(article_id=article_id).order_by("pk").values("pk", "content",
                                                                                          "parent_comment_id"))

    return JsonResponse(ret, safe=False)
View Code

templatetags/my_tags

# -*- coding: utf-8 -*-
# @Time    : 2018/08/04 0004 22:03
# @Author  : Venicid

from django import template
from django.db.models import Count
from blog import models

register = template.Library()

@register.inclusion_tag("blog/classification.html")
def get_classification_style(username):

    user_obj = models.UserInfo.objects.filter(username=username).first()
    blog = user_obj.blog
    cate_list = models.Category.objects.filter(blog=blog).values('pk').annotate(c=Count("article__title")).values_list("title", 'c')
    tag_list = models.Tag.objects.filter(blog=blog).values('pk').annotate(c=Count('article')).values_list('title','c')
    date_list = models.Article.objects.filter(user=user_obj).extra(select={'y_m_date':"date_format(create_time,'%%Y-%%m')"}).values('y_m_date').annotate(c=Count('nid')).values_list('y_m_date', 'c')

    return {'blog':blog,"tag_list":tag_list,"date_list":date_list, "cate_list":cate_list}



@register.simple_tag
def multi_tag(x,y):

    return x*y
View Code

.模板层

article_detail.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="stylesheet" href="/static/bootstrap3/css/bootstrap.css">
    <script src="/static/js/jquery-3.2.1.min.js"></script>
    <style type="text/css">
        .glyphicon-comment {
            vertical-align: -1px;
        }

        #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.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;
        }

        #digg_tips {
            color: red;
            clear: both;
        }

        input.author {
            background-image: url('/static/img/icon_form.gif');
            background-repeat: no-repeat;
            border: 1px solid #ccc;
            padding: 4px 4px 4px 30px;
            width: 230px;
        }

        .comment_content {
            margin-top: 5px;
        }

        .reply_btn {
            color: #369;
            cursor: pointer;

        }
        .comment_item{
            margin-left: 20px;
        }
    </style>
</head>
<body>


<div class="site-header">
    <nav class="navbar navbar-inverse">
        <div class="container-fluid">
            <div class="navbar-header">
                <a class="navbar-brand" href="#">
                    {{ blog.title }}
                </a>
            </div>
            <div class="navbar-header pull-right">
                <a class="navbar-brand" href="#">
                    管理
                </a>
            </div>
        </div>
    </nav>

</div>

<div class="container-fluid">
    <div class="row">
        <div class="col-md-3">
            {% load my_tags %}

            {% get_classification_style username %}
        </div>


        <div class="col-md-8">
            {% csrf_token %}
            <div class="article_info ">
                <h3 class="text-center">{{ article_obj.title }}</h3>
                <div> {{ article_obj.content|safe }}</div>

                <div class="clearfix">
                    <div id="div_digg">
                        <div class="diggit action">
                            <span class="diggnum" id="digg_count">{{ article_obj.up_count }}</span>
                        </div>
                        <div class="buryit action">
                            <span class="burynum" id="bury_count">0</span>
                        </div>
                        <div class="clear"></div>
                        <div class="diggword" id="digg_tips"></div>
                    </div>
                </div>

                <div class="comments list-group">
                    <p class="tree_btn">评论树</p>
                    <div class="comment_tree">

                        <div class="comment_tree">

                        </div>
                    </div>


                    <p>评论列表</p>
                    <ul class="list-group comment_list">
                        {% for comment in comment_list %}
                            <li class="list-group-item">
                                <div class="comment_head">
                                    <a href=""># {{ forloop.counter }}楼</a>&nbsp;&nbsp;
                                    <span>{{ comment.create_time|date:"Y-m-d H:i" }}</span>&nbsp;&nbsp;
                                    <a href="">{{ comment.user.username }}</a>
                                    <span class="pull-right reply_btn" username="{{ comment.user.username }}"
                                          parent_comment_pk="{{ comment.pk }}">回复</span>
                                </div>

                                {% if comment.parent_comment_id %}
                                    <div class="parent_info well">
                                        <p>{{ comment.parent_comment.user.username }}:{{ comment.parent_comment.content }}</p>
                                    </div>
                                {% endif %}

                                <div class="comment_content">
                                    <p>{{ comment.content }}</p>
                                </div>

                            </li>
                        {% endfor %}

                    </ul>

                    <p>发表评论</p>
                    <p>昵称</p><input type="text" id="tbCommentAuthor" class="author" disabled="disabled" size="50"
                                    value="{{ request.user.username }}">
                    <p>评论内容</p>
                    <textarea name="" id="comment_content" cols="60" rows="10"></textarea>
                    <p>
                        <button class="btn btn-default comment_btn">提交评论</button>
                    </p>
                </div>

            </div>
        </div>
    </div>
</div>

<script>
    //点赞请求
    $("#div_digg .action").click(function () {
        var is_up = $(this).hasClass("diggit");

        $obj = $(this).children("span");        // 获取点赞数

        $.ajax({
            url: "/digg/",
            type: "post",
            data: {
                "csrfmiddlewaretoken": $("[name='csrfmiddlewaretoken']").val(),
                "is_up": is_up,
                "article_id":{{ article_obj.pk }}
            },
            success: function (data) {
                // 点赞 +1
                if (data.state) {
                    var val = parseInt($obj.text());
                    $obj.text(val + 1)

                } else {

                    //点赞过了
                    var val = data.handled ? "您已经推荐过了" : "您已经反对过了"
                    $('#digg_tips').html(val)

                    setTimeout(function () {
                        $("#digg_tips").html("")
                    }, 2000)
                }
            }
        })
    })

    //评论请求
    var parent_id = "";

    $('.comment_btn').click(function () {
        var content = $("#comment_content").val()

        if (parent_id) {
            var index = content.indexOf("
")
            content = content.slice(index + 1)
        }

        $.ajax({
            url: '/comment/',
            type: "post",
            data: {
                "csrfmiddlewaretoken": $("[name='csrfmiddlewaretoken']").val(),
                "article_id":{{ article_obj.pk }},
                "content": content,
                "parent_id": parent_id

            },
            success: function (data) {
                console.log(data);

                // dom操作  es6  标签字符串
                var create_time = data.create_time
                var username = data.username
                var content = data.content

                var s = `
                        <li class="list-group-item">
                            <div class="comment_head">
                                <span>${create_time}</span>&nbsp;&nbsp;
                                <a href="">${username}</a>
                                <a class="pull-right">回复</a>
                            </div>

                            <div class="comment_content">
                                <p>${content}</p>
                            </div>
                        </li>`;


                $("ul.comment_list").append(s)

                //清空评论框
                parent_id = ""
                $("#comment_content").val("")
            }
        })


    })

    //回复按钮事件
    $(".reply_btn").click(function () {

        $("#comment_content").focus()
        var val = "@" + $(this).attr("username") + "
";
        $("#comment_content").val(val)

        parent_id = $(this).attr("parent_comment_pk")

    })


    //评论树
    $(".tree_btn").click(function () {
        $.ajax({
            url: "/get_comment_tree/",
            type: "get",
            data: {
                article_id:{{ article_obj.pk }}
            },
            success: function (comment_list) {
                console.log(comment_list)

                $.each(comment_list, function (index, comment_object) {
                    var pk = comment_object.pk
                    var content = comment_object.content
                    var parent_comment_id = comment_object.parent_comment_id

                    var s = '<div class="comment_item" comment_id=' + pk + '><span>' + content + '</span>' + '</div>'

                    if (!parent_comment_id) {
                        //根评论展示
                        $(".comment_tree").append(s)

                    } else {
                        //子评论展示
                        $("[comment_id="+parent_comment_id+ "]").append(s)
                    }

                })
            }
        })
    })

</script>
</body>
</html>
View Code

classifications.html

        <div>
            <div class="panel panel-warning">
                <div class="panel-heading">我的标签</div>
                <div class="panel-body">
                    {% for tag in tag_list %}
                        <p><a href="/{{ username }}/tag/{{ tag.0 }}">{{ tag.0 }}({{ tag.1 }})</a></p>
                    {% endfor %}

                </div>
            </div>
            <div class="panel panel-warning">
                <div class="panel-heading">随笔分类</div>
                <div class="panel-body">
                    {% for cate in cate_list %}
                        <p><a href="/{{ username }}/category/{{ cate.0 }}">{{ cate.0 }}({{ cate.1 }})</a></p>
                    {% endfor %}

                </div>
            </div>
            <div class="panel panel-warning">
                <div class="panel-heading">随笔归档</div>
                <div class="panel-body">
                    {% for date in date_list %}
                        <p><a href="/{{ username }}/archive/{{ date.0 }}">{{ date.0 }}({{ date.1 }})</a></p>
                    {% endfor %}

                </div>
            </div>
        </div>
View Code

home_site.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="stylesheet" href="/static/bootstrap3/css/bootstrap.css">
    <style type="text/css">
        .glyphicon-comment {
            vertical-align: -1px;
        }
    </style>
</head>
<body>
<div class="site-header">
    <nav class="navbar navbar-inverse">
        <div class="container-fluid">
            <div class="navbar-header">
                <a class="navbar-brand" href="#">
                    {{ blog.title }}
                </a>
            </div>
            <div class="navbar-header pull-right">
                <a class="navbar-brand" href="#">
                    管理
                </a>
            </div>
        </div>
    </nav>

</div>

<div class="container-fluid">
    <div class="row">
        <div class="col-md-3">
            <div class="panel panel-warning">
                <div class="panel-heading">我的标签</div>
                <div class="panel-body">
                    {% for tag in tag_list %}
                        <p><a href="/{{ username }}/tag/{{ tag.0 }}">{{ tag.0 }}({{ tag.1 }})</a></p>
                    {% endfor %}

                </div>
            </div>

            <div class="panel panel-warning">
                <div class="panel-heading">随笔分类</div>
                <div class="panel-body">
                    {% for cate in cate_list %}
                        <p><a href="/{{ username }}/category/{{ cate.0 }}">{{ cate.0 }}({{ cate.1 }})</a></p>
                    {% endfor %}

                </div>
            </div>
            <div class="panel panel-warning">
                <div class="panel-heading">随笔归档</div>
                <div class="panel-body">
                    {% for date in date_list %}
                        <p><a href="/{{ username }}/archive/{{ date.0 }}">{{ date.0 }}({{ date.1 }})</a></p>
                    {% endfor %}

                </div>
            </div>
        </div>
        <div class="col-md-8">
            {% for article in article_list %}
                <div class="article-item clearfix">
                    <h5><a href="">{{ article.title }}</a></h5>
                    <div class="article-desc">
                        <span>{{ article.desc }}</span>
                    </div>
                    <div class="small pub_info pull-right ">
                        <span>发布于&nbsp;&nbsp;{{ article.create_time|date:"Y-m-d H:m:s" }}</span>&nbsp;&nbsp;&nbsp;
                        <span class="glyphicon glyphicon-comment">评论({{ article.comment_count }})</span>&nbsp;&nbsp;&nbsp;
                        <span class="glyphicon glyphicon-thumbs-up">点赞({{ article.up_count }})</span>
                    </div>
                </div>
                <hr>
            {% endfor %}

        </div>
    </div>
</div>

</body>
</html>
View Code
原文地址:https://www.cnblogs.com/venicid/p/9428968.html