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; width: 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、显示根评论
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<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> <span>{{ comment.create_time|date:"Y-m-d H:i" }}</span> <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
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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)
//评论请求
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
$('.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> <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("") } }) })
3、子评论
1、回复按钮
2、提交子评论
如果是子评论,就从\n开始取字符串
# 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
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
# 个人站点页面设计 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'),
settings
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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视图
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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)
templatetags/my_tags
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
# -*- 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
.模板层
article_detail.html
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<!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> <span>{{ comment.create_time|date:"Y-m-d H:i" }}</span> <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("\n") 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> <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") + "\n"; $("#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>
classifications.html
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<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>
home_site.html
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<!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>发布于 {{ article.create_time|date:"Y-m-d H:m:s" }}</span> <span class="glyphicon glyphicon-comment">评论({{ article.comment_count }})</span> <span class="glyphicon glyphicon-thumbs-up">点赞({{ article.up_count }})</span> </div> </div> <hr> {% endfor %} </div> </div> </div> </body> </html>