7.博客系统| 评论功能(文章评论,评论的评论)
根评论:对文章的评论;
子评论:对评论的评论;
区别:是否有父评论。
评论的流程:
1构建样式
2提交根评论
3显示根评论
--------render显示
--------Ajax显示
4提交子评论
5显示子评论
--------render显示
--------Ajax显示
6评论树的显示
楼层结构:
111
222
333
树形结构:(有父子关系,楼层的深度、层级)
111
222
444
555
333
1.评论样式
article_detail.html
<div class="clearfix"> //评论和点赞在一行,这是由于点赞和反对做了一个浮动,而评论又在正常文档中,导致它的浮动没有清除;清除浮动在bootstrap里边加个clearfix
<div id=“div_digg...”>
</div>
{# 评论样式#} <div class="comments"> <p>发表评论</p> <p>昵称:<input type="text" id="tbCommentAuthor" class="author" disabled="disabled" size="50" value="{{ request.user.username }}"></p> <p>评论内容:</p> <textarea name="" id="comment_content" cols="60" rows="10"></textarea> //没有必要放到form表单中,因为要Ajax提交;textarea为内联标签,给下面button加了个p标签。 <p> <button class="btn btn-default commment_btn">提交评论</button> // </p> </div>
article_detail.css
input.author{ background-image: url("/static/font/icon_form.gif"); //引入图标 background-repeat: no-repeat; border: 1px solid #ccc; padding: 4px 4px 4px 30px; width: 300px; font-size: 13px; background-position: 3px -3px; }
2.提交(保存)根评论
客户端浏览器发 点赞或者评论按钮给服务端,触发一个事件(给它绑定一个click事件)发送请求,用户一点击就给服务器发请求了,有专门的视图函数接收这个请求,提交评论信息交给comment视图函数,视图函数构建一个评论对象保存到数据库里边,然后再把响应交给回调函数(ajax发完请求给它个回调) ,通过响应结果来判断如果对这个dom操作
views.py
#评论 def comment(request): print(request.POST) article_id = request.POST.get("article_id") #评论文章;user不用传,create_time不用传(评论的时间就是入库的时间),评论表的字段。 pid = request.POST.get("pid") content = request.POST.get("content") #评论内容 user_id = request.user.pk #当前登录人的id comment_obj = models.Comment.objects.create(user_id=user_id, article_id=article_id, content=content,parent_comment_id=pid) //pid是为空的,传给父评论 return HttpResponse("comment")
article_detail.html
//评论请求 $(".commment_btn").click(function () { var pid = "" var content = $("#comment_content").val(); //找到这个标签,val就是取里边的内容 $.ajax({ url:"/comment/", type:"post", data:{ "csrfmiddlewaretoken":$("[name='csrfmiddlewaretoken']").val(), "article_id":"{{ article_obj.pk }}", "content":content, "pid":pid //父评论的id,默认为空。根评论没有 }, success: function (data) { //success回调函数 console.log(data) //清空评论框 $("#comment_content").val(""); } }) })
3.render显示根评论(还有一种是Ajax)
views.py
def article_detail(request, username, article_id): ''' 文章详情页 :param request: :param username: :param article_id: :return: ''' user = models.UserInfo.objects.filter(username=username).first() #个人站点对象 blog = user.blog article_obj = models.Article.objects.filter(pk=article_id).first() #拿到文章对象,要传到模板里边去 # context = get_classification_data(username) comment_list = models.Comment.objects.filter(article_id=article_id) #拿出用户访问那篇文章的id return render(request, "article_detail.html", locals())
article_detail.html
<p>评论列表</p> <ul class="list-group comment_list"> {% for comment in comment_list %} //for循环这个comment_list <li class="list-group-item"> //每次循环生成一个 li标签 <div> <a href="">#{{ forloop.counter }}楼</a> <span>{{ comment.create_time | date:"Y-m-d H:i" }}</span> <a href=""><span>{{ comment.user.username }}</span></a> <a href="" class="pull-right">回复</a> </div> <div class="comment_con"> <p>{{ comment.content }}</p> </div> </li> {% endfor %} </ul>
Ajax显示评论
点提交按钮,发Ajax请求;
Ajax请求在对应的视图函数生成一个comment对象,然后我们响应给它的是comment对象相关的三个键值字典,字典返回给这个回调函数,拿到object对象构建一个标签,把标签字符串样式放在了页面的某个url里边去了完成了dom操作。
success: function (data) { console.log(data); var create_time = data.create_time; //取出这三组变量,把这三个变量插入到下面反引号里边,关于js必须用+不断的去拼接,现在用ES6反引号的语句就很简洁了 var username = data.username; var content = data.content; var s =` //反斜杠 ;构建一个标签字符串像上边那样放到这个样式里边 ;通过dom操作放到这个url中 <li class="list-group-item"> <div> <span>${create_time}/span> //用反引号把字符串包起来,再用${}语法把它插入进来,js里边的只能用+拼,ES6很像python的语法 <a href=""><span>${ username }</span></a> </div> <div class="comment_con"> <p>${content}</p> </div> </li> `; $("ul.comment_list").append(s);
views.py
#评论 def comment(request): print(request.POST) article_id = request.POST.get("article_id") pid = request.POST.get("pid") content = request.POST.get("content") user_id = request.user.pk comment_obj = models.Comment.objects.create(user_id=user_id, article_id=article_id, content=content,parent_comment_id=pid) response = {} #这个就是返回给回调函数的那个data。 response["create_time"] = comment_obj.create_time.strftime("%Y-%m-%d %X") response["username"] = request.user.username #当前评论人 response["content"] = content return JsonResponse(response) #返回一个字典,JsonResponse会帮我们做序列化操作; #return HttpResponse("comment")
先点提交按钮发ajax请求,在对应的视图函数中生成一个对象,响应给它的是comment相关信息-三个键值组成的字典,把字典返回给success回调函数,success拿到那个字典 拿到object对象,构建一个标签,把标签字符串放到页面的某个ul里边去了,完成一个dom操作。(在数据库里边生成一个comment记录,同时在页面上看到提交的这条评论信息了)
4. 回复按钮事件
一点击回复按钮,就@这条评论的评论人; 定光标、赋文本值。
<div> <a href="">#{{ forloop.counter }}楼</a> <span>{{ comment.create_time | date:"Y-m-d H:i" }}</span> <a href=""><span>{{ comment.user.username }}</span></a> <a class="pull-right reply_btn" username= "{{ comment.user.username }}">回复</a> </div>
//回复按钮事件 $(".reply_btn").click(function () { $('#comment_content').focus(); //聚焦 var val="@"+$(this).attr("username")+"\n"; //赋值,父评论的名字 $('#comment_content').val(val) })
5.提交子评论
<div> <a href="">#{{ forloop.counter }}楼</a> <span>{{ comment.create_time | date:"Y-m-d H:i" }} </span> <a href=""><span>{{ comment.user.username }}</span></a> <a class="pull-right reply_btn" username= "{{ comment.user.username }}" comment_pk="{{ comment.pk }}">回复</a> //给每个回复的按钮多加一个自定义的属性 </div>
//评论请求 var pid = ""; $(".commment_btn").click(function () { var content = $("#comment_content").val(); //把评论内容取出来 if(pid){ //判断pid是否为空。 var index=content.indexOf("\n") content = content.slice(index+1) }
//清空评论框
pid = "", //加上它可以清除小bug(你不刷新时,不点击回复按钮之间输入pid则为子评论)
$("#comment_content").val("");
//回复按钮事件
$(".reply_btn").click(function () {
$('#comment_content').focus();
var val="@"+$(this).attr("username")+"\n";
$('#comment_content').val(val);
pid = $(this).attr("comment_pk")
})
6.Ajax显示子评论
应该重新构建标签字符串,从视图中拿到父评论的相关信息,加到s字符串里边
7.评论树的显示
权限展示成树形结构,用递归
评论树的请求数据
<div class="comments list-group"> <p class="tree_btn">评论树</p> <div class="comment_tree"> {# <div class="comment_item" comment_id = "1">#} {# <span>111</span>#} {# <div class="comment_item" comment_id = "4">#} {# <span>444</span>#} {# <div class="comment_item" comment_id = "5">#} {# <span>555</span>#} {# </div>#} {# </div>#} {# </div>#} {# <div class="comment_item" comment_id = "2">#} {# <span>222</span>#} {# </div>#} {# <div class="comment_item" comment_id = "3">#} {# <span>333</span>#} {# </div>#} </div> <script> $(".tree_btn").click(function () { $.ajax({ url:"/get_comment_tree/", type:"get", data:{ data是views里return传来的ret值 article_id : "{{ article_obj.pk }}" }, success:function(data){ console.log(data); $.each(data, 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>
views.py
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)
根评论的序号一定大于子评论的序号
ret = list(models.Comment.objects.filter(article_id=article_id).order_by("pk").values("pk", "content", "parent_comment_id")) return JsonResponse(ret, safe=False)
<script> $.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); } }) } }); {# $(".tree_btn").click(function () {#} {# $.ajax({#} 把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>
评论事务操作
from django.db import transaction
#事务操作 with transaction.atomic(): comment_obj = models.Comment.objects.create(user_id=user_id, article_id=article_id, content=content,parent_comment_id=pid) models.Article.objects.filter(pk=article_id).update(comment_count=F("comment_count")+1)
评论的邮件发送
EMAIL_HOST = 'smtp.exmail.qq.com' #如果是163改成 smtp.163.com
EMAIL_POST = 465
EMAIL_HOST_USER = ' ' #账号
EMAIL_HOST_PASSWORD = ' ' #密码
#DEFAULT_FROM_EMAIL = EMAIL_HOST_USER
EMAIL_USE_SSL = True