BBS - 文章评论
一、文章评论

<div class="comment_region">
<div class="row">
<div class="col-md-8">
<p>昵称:<input type="text" id="tbCommentAuthor" class="author" disabled="disabled" size="50" value="{{ request.user.username }}"></p>
<p>评论内容:</p>
<textarea name="" id="comment-text" cols="63" rows="10"></textarea>
<button class="btn btn-default pull-right comment-btn">提交</button>
</div>
</div> </div>
效果:

事件:
from django.db import transaction def comment(request):
article_id = request.POST.get('article_id')
content =request.POST.get('content')
pid = request.POST.get('pid')
user_id = request.user.pk res = {"state":True} with transaction.atomic(): # 事务 有关联
if not pid: # 提交根评论
obj = Comment.objects.create(user_id = user_id,article_id = article_id,content=content)
else: # 提交子评论
obj = Comment.objects.create(user_id=user_id, article_id=article_id, content=content,parent_comment_id=pid) # comment_count 加 + 1
Article.objects.filter(pk=article_id).update(comment_count=F("comment_count") + 1) res['time'] = obj.create_time.strftime('%Y-%m-%d %H:%M')
res['content'] = obj.content
if obj.parent_comment_id:
res['pid'] = obj.parent_comment_id
res['pidname'] = obj.parent_comment.user.username
return JsonResponse(res) def get_comment_tree(request,article_id):
ret = list(Comment.objects.filter(article_id=article_id).values('pk','content','parent_comment_id','user__username'))
print(ret)
return JsonResponse(ret,safe=False)
path('comment/', views.comment),re_path(r'get_comment_tree/(\d+)', views.get_comment_tree),comment_list = Comment.objects.filter(article_id=article_id)
评论树 评论楼 提交评论事件:
{% extends 'base.html' %}
{% block content %}
<h3 class="text-center">{{ article.title }}</h3>
<div class="content">
{{ article.articledetail.content|safe }}
</div>
<input type="hidden" id="hid_article_pk" value="{{ article.pk }}">
<input type="hidden" id="hid_username" value="{{ request.user.username }}">
<div id="div_digg">
<div class="diggit digg">
<span class="diggnum" id="digg_count">{{ article.up_count }}</span>
</div>
<div class="buryit digg">
<span class="burynum" id="bury_count">{{ article.down_count }}</span>
</div>
<div id="digg_word" class="pull-right"></div>
</div>
<div class="clearfix"></div>
<hr>
<p>评论树</p>
<div class="comment_tree">
<script>
(function () {
$.ajax({
url:'/blog/get_comment_tree/'+$('#hid_article_pk').val(),
success:function (comment_list) {
var comment_html = "";
$.each(comment_list,function (index,comment) {
var username = comment.user__username;
var content = comment.content;
var pk = comment.pk;
var pid = comment.parent_comment_id;
console.log(comment);
s = '<div class="comment_tree_item" id="'+pk+'"><span>'+username+'</span><span>'+content+'</span> </div>'
if(pid){
$('#'+pid).append(s)
}else{
$('.comment_tree').append(s)
}
})
}
})
})()
</script>
</div>
<hr>
<p>评论楼</p>
<ul class="list-group comment_list">
{% for comment in comment_list %}
<li class="list-group-item comment_item">
<div>
<a href="">#{{ forloop.counter }}楼</a>
<span>{{ comment.create_time|date:'Y-m-d H:i'}}</span>
<a href="">{{ comment.user.username }}</a>
<a class="pull-right replay" pk = "{{ comment.pk }}" username="{{ comment.user.username }}">回复</a>
</div>
{% if comment.parent_comment_id %}
<div class="parent_comment_info well">
<a href="">@{{ comment.parent_comment.user.username }}</a>
<span>{{ comment.parent_comment.content }}</span>
</div>
{% endif %}
<div>
<p>{{ comment.content }}</p>
</div>
</li>
{% endfor %}
</ul>
<hr>
<div class="comment_region">
<div class="row">
<div class="col-md-8">
<p>昵称:<input type="text" id="tbCommentAuthor" class="author" disabled="disabled" size="50" value="{{ request.user.username }}"></p>
<p>评论内容:</p>
<textarea name="" id="comment-text" cols="63" rows="10"></textarea>
<button class="btn btn-default pull-right comment-btn">提交</button>
</div>
</div>
</div>
{% csrf_token %}
<script src="/static/js/article_detail.js"></script>
<script type="text/javascript">
var pid = ''; // 空 为 根评论
// 绑定 提交评论事件
$('.comment-btn').click(function () {
if("{{ request.user.username }}"){ // 登录 后操作
// val() input select textarea 这三个可以 取 val()
var article_id = $('#hid_article_pk').val();
if($('#comment-text').val()[0] !== "@"){
pid = ""
}
//@alex\n567
// 获取 子评论 内容
if(pid){
var index = $('#comment-text').val().indexOf('\n');
var content = $('#comment-text').val().slice(index+1)
}else{
var content = $('#comment-text').val();
}
$.ajax({
url:'/blog/comment/',
type:'post',
data:{
article_id:article_id,
content:content,
pid:pid,
csrfmiddlewaretoken:$('input[name="csrfmiddlewaretoken"]').val()
},
success:function (data) {
console.log(data);
if(data.state){ // 提交成功
console.log('-----------',data);
// 根评论 显示
var floor = $('.comment_list .comment_item').length+1;
var ctime = data.time;
var username = $('#hid_username').val();
var content = data.content;
var pname = data.pidname;
if(data.pid){ // 子评论
var s ='<li class="list-group-item comment_item"><div><a href="">#'+floor+'楼</a> <span>'+ ctime+ ' </span> <a href="">'+ username +'</a> </div> <div> ' +
' <div class="parent_comment_info well">\n' +
' <a href="">@'+pname+'</a> \n' +
' </div>'+
'<p>'+content+'</p> </div> </li>';
}else{
// 应该有 2 套 s
var s ='<li class="list-group-item comment_item"><div><a href="">#'+floor+'楼</a> <span>'+ ctime+ ' </span> <a href="">'+ username +'</a> </div> <div> <p>'+content+'</p> </div> </li>';
}
$('.comment_list').append(s);
//清空数据
$('#comment-text').val("");
//清空pid
pid = "";
}
}
})
}else{ // 未登录
location.href = "/login/"
}
});
// 绑定回复 按钮事件
$('.comment_item .replay').click(function () {
// 获取焦点
$('#comment-text').focus();
var val ="@" + $(this).attr('username')+ '\n'; // 回复得人得 名字
$('#comment-text').val(val);
pid = $(this).attr("pk") // pid
})
</script>
{% endblock content%}
article_detail.html
二、知识点
文章评论:
1.提交根评论
2.显示根评论
--- render显示
--- ajax显示
3.提交子评论
4.显示子评论
--- render显示
--- ajax显示
评论楼
评论树
1.
comment_list = Comment.objects.filter(article_id=article_id) 2.事务
with transaction.atomic(): # 事务 数据统一 一致
if not pid: # 提交根评论
obj = Comment.objects.create(user_id = user_id,article_id = article_id,content=content)
else: # 提交子评论
obj = Comment.objects.create(user_id=user_id, article_id=article_id, content=content,parent_comment_id=pid) # comment_count 加 + 1
Article.objects.filter(pk=article_id).update(comment_count=F("comment_count") + 1) 3.a标签
href = "url" 向外链接
href = '#xxx' 向内定位 锚点
不用#xxx,可以用获取焦点:$('#comment-text').focus(); 4.val()
input select textarea 这三个可以 取 .val() 赋值 .val('') 5.js得截取方法:(charAt indexOf slice)
var s = "hello world";
console.log(s.charAt(4)); // o 索引找字符
console.log(s.indexOf('e')); // 1 字符找索引
console.log(s.slice(1,4)); // ell 切片 6.根评论 / 子评论 获取 content
var pid = ''; // 空 为 根评论
if($('#comment-text').val()[0] !== "@"){
pid = ""
} // 获取 子评论 内容 // @alex\n567
if(pid){
var index = $('#comment-text').val().indexOf('\n');
var content = $('#comment-text').val().slice(index+1)
}else{
var content = $('#comment-text').val();
} 7.两个bug:
(1) 先提交子评论,在不刷新页面的情况下再提交根评论会怎样?
//清空数据
$('#comment-text').val("");
//清空pid
pid = "";
(2) 先按回复按钮,然后将文本框中的"@..."手动去除,再点提交会怎样?
if($('#comment-text').val() !== "@"){
pid = ""
} 8.datatime.datetime
时间对象,不能序列化! 字典 列表 可序列化, 对象不可序列化!!
办法:
res['time'] = obj.create_time.strftime('%Y-%m-%d %H:%M')
return JsonResponse(res)
补充:
import datetime
datetime.date
datetime.time
datetime.datetime
datetime.timedelta(days=7) now = datetime.datetime.now() # 对象
now.strftime('%Y-%m')
now.strftime('%Y-%m-%d')
now.strftime('%Y-%m-%d %X')
now.strftime('%Y-%m-%d %H:%M') delta = datetime.timedelta(days=7)
now + delta // 时间相加 9.评论楼
楼层:
<a href="">#{{ forloop.counter }}楼</a>
时间:
<span>{{ comment.create_time|date:'Y-m-d H:i'}}</span>
评论者:
<a href="">{{ comment.user.username }}</a>
回复:(pk username)
<a class="pull-right replay" pk = "{{ comment.pk }}" username="{{ comment.user.username }}">回复</a>
点击回复:
// 绑定回复 按钮事件
$('.comment_item .replay').click(function () {
// 获取焦点
$('#comment-text').focus();
var val ="@" + $(this).attr('username')+ '\n'; // 回复得人得 名字
$('#comment-text').val(val); pid = $(this).attr("pk") // pid
})
判断是否有父评论:
{% if comment.parent_comment_id %}
<div class="parent_comment_info well">
<a href="">@{{ comment.parent_comment.user.username }}</a>
<span>{{ comment.parent_comment.content }}</span>
</div>
{% endif %} 10.ajax显示评论:
var floor = $('.comment_list .comment_item').length+1;
var ctime = data.time; // 时间字符串 因为时间对象json传不过来。
var username = $('#hid_username').val();
var content = data.content; // content 应该从库里取,不应该直接拿前端得数据!因为前端得content是经过 过滤转义 特殊字符后才存到数据库中。
var pname = data.pidname;
if(data.pid){ // 子评论
var s ='<li class="list-group-item comment_item"><div><a href="">#'+floor+'楼</a> ' +
'<span>'+ ctime+ ' </span> <a href="">'+ username +'</a> </div> <div> ' +
' <div class="parent_comment_info well">\n' +
' <a href="">@'+pname+'</a> \n' +
' </div><p>'+content+'</p> </div> </li>';
}else{
// 应该有 2 套 s
var s ='<li class="list-group-item comment_item"><div><a href="">#'+floor+'楼</a> ' +
' <span>'+ ctime+ ' </span> <a href="">'+ username +'</a> </div>' +
' <div> <p>'+content+'</p> </div> </li>';
} $('.comment_list').append(s); 11.评论树:
样式:
111
444
555
222
333 方法:
1.递归!! 麻烦!
自己调自己
有个结束条件
2. comment_list 取出 pid
前端拿到,所有数据
先展示 所有得根评论
子评论 append到某条根评论下 <div pk=1>111
<div pk=4>444
<div pk=5>555</div>
</div>
</div>
<div pk=2>222</div>
<div pk=2>333</div> 知识点:
匿名函数,自执行!
(function () {
alert(111)
})() 应用: $.each(...)
<div class="comment_tree">
<script>
(function () {
$.ajax({
url:'/blog/get_comment_tree/'+$('#hid_article_pk').val(),
success:function (comment_list) { var comment_html = "";
$.each(comment_list,function (index,comment) {
var username = comment.user__username;
var content = comment.content;
var pk = comment.pk;
var pid = comment.parent_comment_id;
console.log(comment);
var s = '<div class="comment_tree_item" id="'+pk+'"><span>'+username+'</span><span>'+content+'</span> </div>' if(pid){ // 子评论
$('#'+pid).append(s) }else{ // 父评论
$('.comment_tree').append(s)
}
})
}
})
})() </script>
</div> 补充:
最新评论指定放到前面: (prepend)
jquery 文档操作:
append appendTo
prepend prependTo
before insertBefore
after insertAfter
clone
replaceWith replaceAll
empty remove detach 样式缩进:
.comment_tree_item{
margin-left: 20px;
} 后台:
def get_comment_tree(request,article_id):
ret = list(Comment.objects.filter(article_id=article_id).values('pk','content','parent_comment_id','user__username'))
print(ret)
return JsonResponse(ret,safe=False) 注意点:
queryset 对象没法做 序列化
办法:
list(quertset) # 强转
BUT:
return JsonResponse(ret)
条件:
In order to allow non-dict objects to be serialized set the safe parameter to False.
修改:
return JsonResponse(ret,safe=False)
BBS - 文章评论的更多相关文章
- $Django 站点:样式--文章--分类文章--文章详情--文章评论点赞--文章评论点赞统计(数据库优化)
<h3>个人站点下的</h3> 知识点 url (r'(?P<username>\w+)/p/(?P<id>\d+)', xiangxi,name='x ...
- django高级之点赞、文章评论及上传文件
目录: 点赞 文章评论 上传文件 保留页面条件 一.点赞 1.所用技术: django model F查询 js应用:$(function () {}); 为文件加载完成执行ready() 方法.等同 ...
- Django博客功能实现—文章评论功能
功能:在A网页提交一个评论Forms_B,提交之后自动刷新页面,能够显示刚刚的画面思路:利用一个已经创建的表单,通过视图让其在网页中表现出来,填写玩信息之后提交,会提交到一个新的视图里面去做接受,接受 ...
- Django博客功能实现—文章评论的显示
功能:在打开文章之后,能在文章下面是显示文章的评论,有父级评论.思路:在文章详情的视图里面,获取这个文章的全部评论,得到显示列表,然后用模板显示出来.步骤:一,在views.py的文章详情中获取评论: ...
- 使用Webdriver刷博客文章评论
package com.zhc.webdriver; import java.util.ArrayList; import java.util.Iterator; import java.util.c ...
- BBS - 文章详细页、点赞、踩灭
一.文章详细页 文章详细页:1.链接:<div><h5><a href="/blog/{{ article.user.username }}/articles/ ...
- python自动化开发-[第二十二天]-bbs多级评论、点赞、上传文件
今日概要: 1.related_name和related_query_name的区别 2.through_fields的用途 3.django的事务提交 4.点赞的动画效果 5.多级评论的原理 6.上 ...
- 编辑bbs文章 获取前端标题内容 和前端内容的方法
- Django - 学习目录
Django 基础 web应用/http协议/web框架 Django简介 Django - 路由层(URLconf) Django - 视图层 Django - 模板层 Django - 模型层 - ...
随机推荐
- 类加载器详解 (转至http://blog.csdn.net/jiangwei0910410003/article/details/17733153)
首先来了解一下字节码和class文件的区别: 我们知道,新建一个java对象的时候,JVM要将这个对象对应的字节码加载到内存中,这个字节码的原始信息存放在classpath(就是我们新建Java工程的 ...
- Linux下密码抓取神器mimipenguin
前有Mimikatz,今有mimipenguin,近日国外安全研究员huntergregal发布了工具mimipenguin,一款Linux下的密码抓取神器,可以说弥补了Linux下密码抓取的空缺. ...
- php date strtotime的用法
1.上个月第一天及最后一天. echo date('Y-m-01', strtotime('-1 month')); echo strtotime(date('Y-m-01 0:00:00', str ...
- linux下如何关闭防火墙、查看当前的状态、开放端口
从配置菜单关闭防火墙是不起作用的,索性在安装的时候就不要装防火墙查看防火墙状态:/etc/init.d/iptables status暂时关闭防火墙:/etc/init.d/iptables stop ...
- strusts annotation
也叫Zero Configuration(零配置),它省去了写xml文件的麻烦,可以直接在类叫进行配置,不用在java文件和xml文件中来回切换. 1.Action级的注解 @ParentPackag ...
- Visual Studio使用技巧,创建自己的代码片段
1.代码片段的使用示例 在编写代码中常会使用代码片段来提高我们的编写代码的效率,如:在Visual Studio中编写一个 for(int i = 0; i < length;i++) { } ...
- 改善C#程序的建议5:引用类型赋值为null与加速垃圾回收
http://www.cnblogs.com/luminji/archive/2011/04/07/2007205.html 在标准的Dispose模式中(见前一篇博客“C#中标准Dispose模式的 ...
- Python图像处理库PIL的ImageSequence模块介绍
ImageSequence模块包括了一个wrapper类,它能够让用户迭代訪问图形序列中每一帧图像. 一.ImageSequence模块的函数 1. Iterator 定义:ImageSequenc ...
- Emulator Error: Could not load OpenGLES emulation library: Could not load DLL!
Copy the file below from SDK\tools\lib to SDK\tools. libEGL_translator.dlllibGLES_CM_translator.dlll ...
- 查看用户信息:w
w命令有两个用途: (1) 用于查看当前系统负载(2) 用于查看当前登录用户的行为和信息,执行这个命令可以得知当前登入系统的用户有哪些人,以及他们正在执行哪些程序 [root@localhost ~] ...