评论详解
首先呢,评论有分为根评论和子评论,所谓的根评论呢,就是对当前文章进行评论,子评论呢,就是对别人的评论进行再次评论,怎么区别是根评论还是子评论呢,pid来判断
表结构设计:
class Comment(models.Model):
'''评论'''
nid=models.AutoField(primary_key=True)
articel=models.ForeignKey(to='Personal_Article',to_field='nid',verbose_name='评论文章',on_delete=models.CASCADE)
comment_detail=models.TextField(max_length=200,verbose_name='评论内容')
user=models.ForeignKey(to='UserInfo',to_field='nid',verbose_name='评论用户',on_delete=models.CASCADE)
comment_time=models.DateTimeField(auto_now_add=True,verbose_name='评论时间')
parrent_comment=models.ForeignKey(to='self',to_field='nid',verbose_name='父评论',null=True,blank=True,on_delete=models.CASCADE)
##有无父评论
class Meta:
verbose_name_plural='评论表'
def __str__(self):
return self.comment_detail
根节点评论:
pid为空的时候
前端代码实现:(全部评论)
<div class="comment_list_div" style="padding-top: 50px">
<p>评论列表</p>
<ul class="comment_list">
{% for obj in all_comment %}
<li class="list-group-item">
<div>
<a href="">#{{ forloop.counter }}楼</a>
<span style="color: gray">{{ obj.comment_time|date:'Y-m-d H:i' }}</span>
<a href="">{{ obj.user.username }}</a>
<a apply_username="{{ obj.user.username }}" replay_pid="{{ obj.pk }}"
class="pull-right apply">回复</a>
</div>
<!--假如obj是由父评论的时候,就显示出父评论出来-->
{% if obj.parrent_comment %}
<div class="son_comment well">
<p>@{{ obj.parrent_comment.user.username }} 评论:{{ obj.parrent_comment.comment_detail }}</p>
</div>
{% endif %}
<!--显示出子评论或者是根评论(正常的评论)-->
<div class="comment_detail">
<p>{{ obj.comment_detail }}</p>
</div>
</li>
{% endfor %}
</ul>
</div>
评论框:
<div id="comment_form_container">
<script type="text/javascript" src="//mention.cnblogs.com/bundles/mention.js?id=20160615"></script>
<div id="commentform_title">发表评论</div>
<span id="tip_comment" style="color:Red"></span>
<p>
昵称:<input type="text" id="tbCommentAuthor" class="author" disabled="disabled" size="50"
value="{{ request.session.username }}">
</p>
<div class="commentbox_main">
<div class="commentbox_title">
<div class="commentbox_title_left">评论内容:</div>
</div>
<div class="clear"></div>
<textarea id="tbCommentBody" class="comment_textarea"></textarea>
</div>
<p id="commentbox_opt">
<input id="btn_comment_submit" type="button" class="comment_btn" value="提交评论">
<span id="span_comment_canceledit" style="display:none"><a href="javascript:void(0);"
onclick="return CancelCommentEdit()">不改了</a></span>
<a href="javascript:void(0);" onclick="return logout();">退出</a>
<a id="commentbox_opt_sub" href="javascript:void(0);" title="订阅后有新评论时会邮件通知您"
onclick="commentManager.Subscribe()">订阅评论</a>
</p>
<div id="tip_comment2" style="color:Red"></div>
<p>
[Ctrl+Enter快捷键提交]
</p>
<div style="display:none">
<span id="comment_edit_id"></span><span id="span_parentcomment_id"></span>
<span id="span_parent_id"></span>
<span id="span_comment_replyto"></span>
<span id="span_comment_posted"></span>
</div>
</div>
{% csrf_token %}
js代码如下:(评论)
<script>//评论的时候,根评论
var pid = "";//默认一提交评论就是根评论
//回复的时候,下面是是子评论,pid值也发生变化,子评论
//点点击这个回复按钮的时候,就可以跳到文本框进行回复
$('.list-group-item .apply').click(function () {
$("#tbCommentBody").focus();//跳到这个框里面,光标,让光标集中到哪里
var apply_username = "@" + $(this).attr("apply_username") + $(this).attr("replay_pid") + "\n";//得到当前的要回复的用户名,换行,光标下一行
$('.comment_textarea').val(apply_username);
//得到是谁进行的评论
pid = $(this).attr("replay_pid");//拿到当前要回复的pk,相同的人评论pk可能不同
alert("pid改变", pid)//var是局部变量
});
$(".comment_btn").click(function () {
//提交评论内容
var article_id = '{{ obj.article.pk }}';//当前文章的pk
var content = $('.comment_textarea').val();
$.ajax({
url: '/blog/article/comment/',
type: 'post',
data: {
'article_id': article_id,
'pid': pid,
'content': content,
csrfmiddlewaretoken: $("[name='csrfmiddlewaretoken']").val()
},
success: function (data) {
alert('suc');
var name = data.msg.username;
var content = data.msg.content;
var comment_time = data.msg.create_time;
//dom操作
var add_ele = "<li class=\"list-group-item\">\n" +
" <div>\n" +
" <span style=\"color: gray\">" + comment_time + "</span> \n" +
" <a href=\"\">" + name + "</a>\n" +
" </div>\n" +
" <div class=\"comment_detail\">\n" +
" <p>" + content + "</p>\n" +
" </div>\n" +
" </li>";
$('.comment_list').append(add_ele);//dom操作
//当提交评论之后,当前的文本框清除掉评论内容
$('.comment_textarea').val('');
pid = '';//提交完之后pid清空,正常又是跟评论,否则pid有值,还是子评论
}
});
});
</script>
如果是pid为空的时候,说明是根节点评论
就发送ajax请求,传3个参数过去,一个是当前文章的id,还有评论的内容,还有pid,还有要带一个csrf过去,否则post请求是会被403禁止的csrfmiddlewaretoken: $("[name='csrfmiddlewaretoken']").val()
当前文章的pk值obj,article.pk,
评论的内容var content = $('.comment_textarea').val();
pid=""
如果是子节点评论的话
pid就是当前要对它进行评论的用户(已经评论过),就是自己点击回复该评论的用户,评论内容和文章的id是和上面的根节点评论的完全相同,pid就是当前评论的pk值,具有唯一性,可以去comment里面可以查看到该评论,pk
//回复的时候,下面是是子评论,pid值也发生变化,子评论
//点点击这个回复按钮的时候,就可以跳到文本框进行回复
$('.list-group-item .apply').click(function () {
$("#tbCommentBody").focus();//跳到这个框里面,光标,让光标集中到哪里
var apply_username = "@" + $(this).attr("apply_username") + $(this).attr("replay_pid") + "\n";//得到当前的要回复的用户名,换行,光标下一行
$('.comment_textarea').val(apply_username);
//得到是谁进行的评论
pid = $(this).attr("replay_pid");//拿到当前要回复的pk,相同的人评论pk可能不同
alert("pid改变", pid)//var是局部变量
});
dom操作:
dom操作就是临时的将当前评论的数据加到评论列表的下面,再次刷新的时候就会正常显示出来了
如果是对当前文章的评论就是根评论,ajax会接收到参数data,里面有username(当前登录的用户,也就是评论的了),content(当前用户评论的内容) ,create_time(当前评论的时间),此时parrent_comment_id是为空的,接收到之后呢,进行处理,下面进行判断如果parrent_comment存在值的话,当前父评论下面就有值了
评论列表
<!--假如obj是由父评论的时候,就显示出父评论出来--> {% if obj.parrent_comment %} <div class="son_comment well"> <p>@{{ obj.parrent_comment.user.username }} 评论:{{ obj.parrent_comment.comment_detail }}</p> </div> {% endif %}
下面的dom操作,不管是不是根节点评论还是子节点评论都是放在评论列表的最下面显示出来,一样的处理方式
success: function (data) {
alert('suc');
var name = data.msg.username;
var content = data.msg.content;
var comment_time = data.msg.create_time;
//dom操作
var add_ele = "<li class=\"list-group-item\">\n" +
" <div>\n" +
" <span style=\"color: gray\">" + comment_time + "</span> \n" +
" <a href=\"\">" + name + "</a>\n" +
" </div>\n" +
" <div class=\"comment_detail\">\n" +
" <p>" + content + "</p>\n" +
" </div>\n" +
" </li>";
$('.comment_list').append(add_ele);//dom操作
//当提交评论之后,当前的文本框清除掉评论内容
$('.comment_textarea').val('');
pid = '';//提交完之后pid清空,正常又是根评论,否则pid有值,还是子评论
}
});
});
后端实现代码如下:
文章详情表(article_detail)
def Article_Detail(request, article_name):
# 得到所有的评论,当前文章的所有评论
article_pk = models.Personal_Article.objects.filter(title=article_name).get().pk
##去评论表里面去评论
all_comment = models.Comment.objects.filter(articel_id=article_pk).all()
##判断存不存在当前这个文章
all_tilte = models.Personal_Article.objects.values('title')
for title in all_tilte:
if article_name == title['title']:
detail_obj = models.Article_detail.objects.filter(article__title=article_name).all()
return render(request, 'article_detail.html', locals())
return HttpResponse('not find 这个文章')
comment部分如下:
# 评论
def Comment(request):
print('进入评论页面')
res = {'status': 0, 'msg': {}}
print(request.POST)
username = request.session['username']
article_id = request.POST.get('article_id')
content = request.POST.get('content')
user_pk = models.UserInfo.objects.filter(username=username).get().pk
pid = request.POST.get('pid')
##创建数据
print('pid', pid, type(pid))
if pid:
print('子评论', content, type(content))
# content = content.split('\n', 1)[1:][0] ##去掉前面的@yunxin\n评论内容
print(content)
res['msg']['pid'] = pid ##dom也可以是子评论下面呀
obj = models.Comment.objects.create(articel_id=article_id, comment_detail=content, user_id=user_pk,
parrent_comment_id=pid)
else:
print('根评论')
##根评论,pid=0
obj = models.Comment.objects.create(articel_id=article_id, comment_detail=content, user_id=user_pk)
# dom操作需要返回的值
res['msg'] = {
'username': username,
'content': content,
'create_time': obj.comment_time.time()
}
return JsonResponse(res)
后端代码接收到之后呢,就对ajax请求过来的数据进行校验,判断当前的pid是否存在
如果是存在的话,说明是根节点评论
obj = models.Comment.objects.create(articel_id=article_id, comment_detail=content, user_id=user_pk),后面不用加上parrent_coment_id
如果是子节点评论的话
pid就存在值,此时还需要对这个评论内容进行切割@yunxin\n 内容,去掉前面的内容,content = content.split('\n', 1)[1:][0]
创建评论的数据到数据库里面:
obj = models.Comment.objects.create(articel_id=article_id, comment_detail=content, user_id=user_pk,parrent_comment_id=pid)
返回前台数据做dom处理:
返回的数据有username(当前评论的用户),content(当前评论的内容),create_time(评论的时间),dom操作的时候可以在下面创建一条评论的数据出来,在此刷新的时候就会正常显示
前端完整代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<link rel="stylesheet" href="/static/components/my_style.css">
<link rel="stylesheet" href="/static/components/bootstrap.min.css">
<script src="/static/components/jquery-3.1.1.js"></script>
</head>
<body>
{% for obj in detail_obj %}
<div class="header_body">
<h1 id="article_title">{{ obj.article.title }}</h1>
<span>上传作者:{{ obj.article.user.username }}</span>
<span>发布时间:{{ obj.article.create_time }}</span>
</div>
{{ obj.content|safe }}
<div id="div_digg " class="pull-right" style="width: 120px">
<div class="diggit action">
<span class="diggnum" id="digg_count">{{ obj.article.up_count }}</span>
</div>
<div class="buryit action" style="left: 200px">
<span class="burynum" id="bury_count">{{ obj.article.down_count }}</span>
</div>
<div class="clear "></div>
<div class="diggword" id="digg_tips" style="color: red">
</div>
</div>
<div class="comment_list_div" style="padding-top: 50px">
<p>评论列表</p>
<ul class="comment_list">
{% for obj in all_comment %}
<li class="list-group-item">
<div>
<a href="">#{{ forloop.counter }}楼</a>
<span style="color: gray">{{ obj.comment_time|date:'Y-m-d H:i' }}</span>
<a href="">{{ obj.user.username }}</a>
<a apply_username="{{ obj.user.username }}" replay_pid="{{ obj.pk }}"
class="pull-right apply">回复</a>
</div>
<!--假如obj是由父评论的时候,就显示出父评论出来-->
{% if obj.parrent_comment %}
<div class="son_comment well">
<p>@{{ obj.parrent_comment.user.username }} 评论:{{ obj.parrent_comment.comment_detail }}</p>
</div>
{% endif %}
<!--显示出子评论或者是根评论(正常的评论)-->
<div class="comment_detail">
<p>{{ obj.comment_detail }}</p>
</div>
</li>
{% endfor %}
</ul>
</div>
<div id="comment_form_container">
<script type="text/javascript" src="//mention.cnblogs.com/bundles/mention.js?id=20160615"></script>
<div id="commentform_title">发表评论</div>
<span id="tip_comment" style="color:Red"></span>
<p>
昵称:<input type="text" id="tbCommentAuthor" class="author" disabled="disabled" size="50"
value="{{ request.session.username }}">
</p>
<div class="commentbox_main">
<div class="commentbox_title">
<div class="commentbox_title_left">评论内容:</div>
</div>
<div class="clear"></div>
<textarea id="tbCommentBody" class="comment_textarea"></textarea>
</div>
<p id="commentbox_opt">
<input id="btn_comment_submit" type="button" class="comment_btn" value="提交评论">
<span id="span_comment_canceledit" style="display:none"><a href="javascript:void(0);"
onclick="return CancelCommentEdit()">不改了</a></span>
<a href="javascript:void(0);" onclick="return logout();">退出</a>
<a id="commentbox_opt_sub" href="javascript:void(0);" title="订阅后有新评论时会邮件通知您"
onclick="commentManager.Subscribe()">订阅评论</a>
</p>
<div id="tip_comment2" style="color:Red"></div>
<p>
[Ctrl+Enter快捷键提交]
</p>
<div style="display:none">
<span id="comment_edit_id"></span><span id="span_parentcomment_id"></span>
<span id="span_parent_id"></span>
<span id="span_comment_replyto"></span>
<span id="span_comment_posted"></span>
</div>
</div>
{% csrf_token %}
<script>
//点赞的或踩的时候
$('div .action').click(function () {
var is_up = $(this).hasClass('diggit');//这边的is_up是布尔值,但是在传过来的时候不是['True']的形式
//判断当前点击的是否有这个属性赞或者是踩
{#var article_title=$("#article_title").text()#}
var article_id = '{{ obj.article.pk }}';//取到当前点赞或踩的文章值
$.ajax({
url: '/blog/article/up_down/',
type: "post",
data: {
csrfmiddlewaretoken: $("[name='csrfmiddlewaretoken']").val(),
is_up: is_up,
'article_id': article_id,//需要3个参数,一个是赞或者踩,一个是文章id,一个是赞或者踩的用户
},
success: function (data) {
if (data.status) {
//点赞踩成功
if (is_up) {
var val = $("#digg_count").text();//字符串
val = parseInt(val) + 1;
$("#digg_count").text(val);//点赞成功
} else {
var val = $("#bury_count").text();//字符串
val = parseInt(val) + 1;
$("#bury_count").text(val);//点赞成功
}
} else {
if (data.msg) {
$(".diggword").html('您已经赞过');
} else {
$(".diggword").html('您已经踩过');
}
setTimeout(function () {
$(".diggword").html('');
}, 5000)//设置时间消失
}
}
})
});
//评论的时候,根评论
var pid = "";//默认一提交评论就是根评论
//回复的时候,下面是是子评论,pid值也发生变化,子评论
//点点击这个回复按钮的时候,就可以跳到文本框进行回复
$('.list-group-item .apply').click(function () {
$("#tbCommentBody").focus();//跳到这个框里面,光标,让光标集中到哪里
var apply_username = "@" + $(this).attr("apply_username") + $(this).attr("replay_pid") + "\n";//得到当前的要回复的用户名,换行,光标下一行
$('.comment_textarea').val(apply_username);
//得到是谁进行的评论
pid = $(this).attr("replay_pid");//拿到当前要回复的pk,相同的人评论pk可能不同
alert("pid改变", pid)//var是局部变量
});
$(".comment_btn").click(function () {
//提交评论内容
var article_id = '{{ obj.article.pk }}';//当前文章的pk
var content = $('.comment_textarea').val();
$.ajax({
url: '/blog/article/comment/',
type: 'post',
data: {
'article_id': article_id,
'pid': pid,
'content': content,
csrfmiddlewaretoken: $("[name='csrfmiddlewaretoken']").val()
},
success: function (data) {
alert('suc');
var name = data.msg.username;
var content = data.msg.content;
var comment_time = data.msg.create_time;
//dom操作
var add_ele = "<li class=\"list-group-item\">\n" +
" <div>\n" +
" <span style=\"color: gray\">" + comment_time + "</span> \n" +
" <a href=\"\">" + name + "</a>\n" +
" </div>\n" +
" <div class=\"comment_detail\">\n" +
" <p>" + content + "</p>\n" +
" </div>\n" +
" </li>";
$('.comment_list').append(add_ele);//dom操作
//当提交评论之后,当前的文本框清除掉评论内容
$('.comment_textarea').val('');
pid = '';//提交完之后pid清空,正常又是跟评论,否则pid有值,还是子评论
}
});
});
</script>
{% endfor %}
</body>
</html>
属性选择器:
自定义的属性选择comment_tree_id=54 自定制属性comment_tree_id
$("[comment_tree_id="+pid+"]").append(ele)
获取到当前属性值:
$(this).attr("replay_id")
详解:
首先呢,在前端页面呢有根节点的评论,就是pid为空的,当你直接评论的时候,就是对当前的文章进行评论,点击提交评论的按钮的时候,会触发ajax的操作,到后端这里呢,会创建一条评论数据
而这个评论数据中的文章id就是当前评论的文章,评论的用户就是当前登录的用户,父节点id此时为空呀,当创建完数据只有,会返回数据到前端,执行dom操作,将当前根节点评论appned到评论列表
的下面,当页面再次实现的时候呢,就会自然正常显示了
obj = models.Comment.objects.create(articel_id=article_id, comment_detail=content, user_id=user_pk)
而这个子节点评论呢,就有这个pid,而这个pid的获取呢,就是通过你点击的回复来看的,这个回复对应的就是自己的评论的pk值,自定义属性replay_pid="{{ obj.pk }},当点击这个回复的时候,
在点击提交评论的时候,$("#tbCommentBody").focus(),这个光标的焦点就集中到了评论框里面呀,而这个pid的值就发生改变了,pid = $(this).attr("replay_pid");就是当前this的属性
值,就获取到了这个评论的pk,然后呢通过ajax发送数据过去,在后端呢创建数据的时候,就多了一个父节点id值,这个呢,就是当前评论的pk值,所以呢,当ajax返回数据到前端的时候,就一样
的dom操作,当再次刷新页面的时候,在前端循环所有的评论的时候,也就是评论列表的地方就会进行判断当前的评论是否存在父节点id,如果存在呢,说明是子评论,获取到呢,然后显示在父评论的下面
obj = models.Comment.objects.create(articel_id=article_id, comment_detail=content, user_id=user_pk,parrent_comment_id=pid)
评论树:
def CommentTree(request,article_pk): array=list(models.Comment.objects.filter(articel_id=article_pk). values('pk','comment_detail','parrent_comment_id','comment_time','user__username')) ##注明一下,这里只能是value,不能是all,然后list,之后得到的是list列表的类型,[{pk:xx,comment_detail:xx},{},{},{}] print(array)##确定唯一的这个文章对应的评论 return JsonResponse(array,safe=False)
这个原理和循环判断有没有父节点id是一样的,不过多讲解了