实现流程
根评论:基于文章的评论
子评论:对于评论的评论
两者区别:是否有父评论
对评论进行树形显示。
构建样式
代码
前端代码:
<div class="clearfix">
<div id="div_digg">
{% csrf_token %}
<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">{{ article_obj.down_count }}</span>
</div>
<div class="clear"></div>
<div class="diggword" id="digg_tips" style="color: red;"></div>
</div>
</div>
<div class="comments">
<p>发表评论</p>
<p>
<p>昵称: <input type="text" id="tbCommentAuthor" class="author" disabled="disabled" size="50" value="{{ request.user.username }}"></p>
</p>
<p>评论内容:</p>
<textarea name="" id="" cols="60" rows="10"></textarea>
<p><button class="btn btn-default comment_btn">提交评论</button></p>
</div>
效果展示
根评论提交和render显示
render显示,在用户提交评论后,点击刷新页面后显示所有根评论的内容。
发送请求到article_detail 视图函数中会返回数据库中所有评论数据,再进行页面渲染。
代码
前端代码
<div class="comments">
<p>评论列表</p>
<ul class="comment_list">
{% for comment in comment_list %}
<li class="list-group-item">
<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>
<p>发表评论</p>
<p>
<p>昵称: <input type="text" id="tbCommentAuthor" class="author" disabled="disabled" size="50" value="{{ request.user.username }}"></p>
</p>
<p>评论内容:</p>
<textarea name="" id="comment_content" cols="60" rows="10"></textarea>
<p><button class="btn btn-default comment_btn">提交评论</button></p>
</div>
<script>
// 评论
$(".comment_btn").click(function (){
var pid = ""
var content = $("#comment_content").val()
$.ajax({
url:"/comment/",
type:"post",
data:{
"csrfmiddlewaretoken": $("[name='csrfmiddlewaretoken']").val(),
"article_id": {{ article_obj.pk }},
"content": content,
"pid": pid
},
success: function(data) {
console.log(data)
// 清空输入评论框
$("#comment_content").val("")
}
})
})
</script>
{% endblock %}
后端代码:
def article_detail(request, username, article_id):
user = UserInfo.objects.filter(username=username).first()
article_obj = models.Article.objects.filter(pk=article_id).first()
blog = models.Blog.objects.filter(userinfo__username=username).first()
comment_list = models.Comment.objects.filter(article_id=article_id)
return render(request, 'article_detail.html', locals())
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
models.Comment.objects.create(
user_id=user_id,
article_id=article_id,
content=content,
parent_comment_id=pid
)
return HttpResponse('ok')
效果展示
ajax显示根评论
用户点击提交评论后,评论内容应局部刷新到评论列表。
代码
前端代码:
<script>
// 评论
$(".comment_btn").click(function (){
var pid = ""
var content = $("#comment_content").val()
$.ajax({
url:"/comment/",
type:"post",
data:{
"csrfmiddlewaretoken": $("[name='csrfmiddlewaretoken']").val(),
"article_id": {{ article_obj.pk }},
"content": content,
"pid": pid
},
success: function(data) {
console.log(data)
var create_time = data.create_time
var username = data.username
var content = data.content
s = `
<li class="list-group-item">
<div>
<span>${create_time}</span>
<a href=""><span>${username}</span></a>
</div>
<div class="comment_con">
<p>${content}</p>
</div>
</li>
`
$(".comment_list").append(s)
// 清空输入评论框
$("#comment_content").val("")
}
})
})
</script>
后端代码:
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
# 事务操作
with transaction.atomic():
commnet_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)
response = {}
response['create_time'] = commnet_obj.create_time
response['username'] = request.user.username
response['content'] = commnet_obj.content
return JsonResponse(response)
效果展示
回复按钮事件绑定
在用户点击某一个评论后的回复按钮时,光标应跳转到评论内容的输入框中,并且输入框里的第一行内容为**@该条评论的发布者名字**。
代码
为回复按钮添加一个username属性,就可以直接拿到该条评论的作者是谁。
<a class="pull-right reply_btn" username="{{ comment.user.username }}">回复</a>
<script>
// 评论回复
$(".reply_btn").click(function (){
var val = "@" + $(this).attr("username") + "\n"
// 焦点到输入框
$('#comment_content').focus();
$('#comment_content').val(val);
})
</script>
效果展示
子评论提交和render方式显示子评论
代码
前端代码
<ul class="comment_list">
{% for comment in comment_list %}
<li class="list-group-item">
<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>
{% if comment.parent_comment_id %}
<div class="pid_info well">
<p>
{{ comment.parent_comment.user.username }}:{{ comment.parent_comment.content }}
</p>
</div>
{% endif %}
<div class="comment_con">
<p>{{ comment.content }}</p>
</div>
</li>
{% endfor %}
</ul>
<script>
// 评论
var pid = ""
$(".comment_btn").click(function (){
var content = $("#comment_content").val()
if (pid){
// 子评论,对content内容截断第一行
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,
"pid": pid
},
success: function(data) {
console.log(data)
var create_time = data.create_time
var username = data.username
var content = data.content
s = `
<li class="list-group-item">
<div>
<span>${create_time}</span>
<a href=""><span>${username}</span></a>
</div>
<div class="comment_con">
<p>${content}</p>
</div>
</li>
`
$(".comment_list").append(s)
// 清空输入评论框
$("#comment_content").val("")
// 清空pid
pid=""
}
})
})
</script>
评论的评论树显示
思路:
- 添加ajax事件,当页面刷新的时候向后台发送请求获取当前文章的所有数据
- 先将根评论添加到界面
- 再将子评论添加到根评论的下面
关键的是如何找到其根评论,在每一个评论的标签中都有一个comment_id的属性,这样就可以找到其对应父评论的将子评论append到父评论下即可。
代码
前端代码:
<p class="tree_btn">评论树</p>
<div class="comment_tree">
</div>
<script>
<!-- // 评论树-->
$.ajax({
url:"/get_comment_tree/",
type:"get",
data:{
"article_id":"{{ article_obj.pk }}"
},
success: function(data){
console.log(data)
$.each(data, function(index, comment_obj){
var pk = comment_obj.pk
var content = comment_obj.content
var parent_comment_id = comment_obj.parent_comment_id
var s = `<div class="comment_item" comment_id="${pk}">
<p>${content}</p>
</div>`
if (!parent_comment_id){
// 根评论处理
$(".tree_btn").append(s)
}
else{
$("[comment_id="+parent_comment_id+"]").append(s)
}
})
}
})
</script>
css样式
.comment_item{
margin-left: 20px;
}
后端代码:
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)
效果展示
新评论邮件通知作者
有评论的时候,将评论信息以邮件的方式通知到作者。新建一个线程来进行发送邮件,防止防落带宽延迟导致评论信息刷新慢。
代码
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
# 事务操作
with transaction.atomic():
commnet_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)
response = {}
response['create_time'] = commnet_obj.create_time
response['username'] = request.user.username
response['content'] = commnet_obj.content
# 发送邮件
article_obj = models.Article.objects.filter(pk=article_id).first()
t = threading.Thread(target=send_mail, args=(
"您的文章{0}新增一条评论内容".format(article_obj.title),
content,
settings.EMAIL_HOST_USER,
['282470196@qq.com'],)
)
t.start()
return JsonResponse(response)
settings.py
# 邮件配置
EMAIL_HOST = 'smtp.exmail.qq.com' # 163改成smtp.163.com
EMAIL_PORT = 465
EMAIL_HOST_USER = ''
EMAIL_HOST_PASSWORD = ''
DEFAULT_FROM_EMAIL = EMAIL_HOST_USER
EMAIL_USER_SSL = True