博客系统之评论树与评论楼相关操作
一、点赞与点灭
思路分析:
1、给点赞和点灭添加事件
2、发送ajax请求
3、实现点灭的时候需要在给表里面增加一个字段,不用再创建一张表来存储了
注意点赞和点灭。评论等涉及到的一个知识点:
事务:要么同时发生,要么同时不发生(就像你到银行里面取钱一样,你输了200,而取款机却把你的钱扣了,不给你钱。那你就不高兴了)
所以,当用户进行点赞的时候,创建赞示例和点赞数必须是同时发生的,当你点完一次赞,数据库里的赞和点赞数都是要增加的。这就用到事务了。
说起事务,在sql里面学过,那么怎么在django中实现事务呢?
1、首先需要导入:
from django.db import transaction
2、如下
with transaction.atomic(): print("========") # 当有人点赞的时候数据库的数据肯定会多一条 models.Article_poll.objects.create(user_id=user_id,article_id=article_id) # 并且点赞数加1 models.Article.objects.filter(id=article_id).update(up_count=F("up_count")+1)
下面是实现点赞和点灭的具体操作
class Article_poll(models.Model): '''文章点赞表''' time = models.DateTimeField(verbose_name="点赞时间",auto_now_add=True) article = models.ForeignKey(to="Article",verbose_name="点赞文章",null=True,blank=True) #一个文章可以有多个赞 user = models.ForeignKey(to="UserInfo",verbose_name="点赞人",null=True,blank=True) is_positive = models.BooleanField(default=1,verbose_name="点赞或踩") #如果为True的时候代表式赞,如果是False的时候代表式
//用ajax实现点赞 $(".diggit").click(function () { if ($(".info").attr("user_name")) { //登录状态 $.ajax({ url: "/blog/poll/", type: "POST", data: { csrfmiddlewaretoken: $("[name='csrfmiddlewaretoken']").val(), article_id: "{{ article_obj.id }}", //需要知道用户对那篇文章点赞 is_positive: "1" }, success: function (data) { var data = JSON.parse(data); console.log(data["tishi"]); //falseif (data["state"]) { var val = parseInt($("#digg_count").html()) + 1; $("#digg_count").html(val); $(".message").html("点赞成功").css("color", "red"); } else if (data["is_repeat"]) { $(".message").html("不能重复点赞").css("color", "red") } } }) } else { alert(location.pathname); //拿到的是路径部分 location.href = "/login/?next=" + location.pathname } });
点灭
1 //用ajax实现点灭 2 $(".buryit").click(function () { 3 if ($.cookie("username")) { //登录状态,和点赞一样,是第二种方式,建议用cookie的方式 4 $.ajax({ 5 url: "/blog/poll/", 6 type: "POST", 7 headers: {"X-CSRFToken": $.cookie('csrftoken')}, 8 data: { 9 csrfmiddlewaretoken: $("[name='csrfmiddlewaretoken']").val(), 10 article_id: "{{ article_obj.id }}", 11 is_positive: "0" 12 }, 13 success: function (data) { 14 var data = JSON.parse(data); 15 console.log(data); 16 console.log(data.tishi); 17 if (data["state"]) { 18 var val = parseInt($("#bury_count").html()) + 1; 19 $("#bury_count").html(val); 20 $(".message").html("踩成功").css("color", "red"); 21 } 22 else if (data["is_repeat"]) { 23 $(".message").html("不能重复点").css("color", "red") 24 } 25 } 26 }) 27 } 28 else { 29 alert(location.pathname); //拿到的是路径部分 30 location.href = "/login/?next=" + location.pathname 31 } 32 });
views.py
1 def poll(request): 2 '''点赞和点灭操作''' 3 pollresponse = {"state": True, "is_repeat": None} # 如果state为True代表访问成功,is_repeat为是否重复 4 5 article_id = request.POST.get("article_id") 6 user_id = request.user.nid 7 is_positive = request.POST.get("is_positive") 8 print("===========",is_positive) 9 # print(article_id,user_id) 10 print("user",request.user.username) 11 # if models.Article_poll.objects.filter(user_id=user_id,article_id=article_id): 12 # #如果这条记录存在,就不能在提交了,这是没有建联合唯一的时候实现的方法,有了联合唯一就不用判断了 13 # pollresponse["state"] = False 14 # pollresponse["is_repeat"] = True 15 # else: 16 try: 17 '''解决联合唯一的问题,如果是联合唯一你一创建就会报错,捕获一下走except的内容''' 18 if is_positive=="1": #点赞 19 with transaction.atomic(): 20 print("========") 21 # 当有人点赞的时候数据库的数据肯定会多一条 22 models.Article_poll.objects.create(user_id=user_id,article_id=article_id) 23 # 并且点赞数加1 24 models.Article.objects.filter(id=article_id).update(up_count=F("up_count")+1) 25 else: #点灭 26 with transaction.atomic(): 27 print("=======zzzz") 28 # 当有人点赞的时候数据库的数据肯定会多一条 29 models.Article_poll.objects.create(user_id=user_id, article_id=article_id) 30 # 并且点赞数加1 31 models.Article.objects.filter(id=article_id).update(down_count=F("down_count")+1) 32 except: 33 pollresponse["state"] = False 34 pollresponse["is_repeat"] = True 35 return HttpResponse(json.dumps(pollresponse))
三、评论树
1、===============构建评论树=================
首先回顾一下可变类型和不可变数据类型
可变数据类型:[],{}
不可变数据类型:字符串,元组,数字
#吧father_comment_id为NULL的筛选出来,father_comment_id为none的说明是根评论(对文章的评论),有值就是子评论(对评论的评论) comment_list=[ {'id': 154, 'content': '发个', 'farther_comment_id': None}, {'id': 155, 'content': '你好啊', 'farther_comment_id': None}, {'id': 156, 'content': '嘻嘻', 'farther_comment_id': 155}, {'id': 157, 'content': '哎呦', 'farther_comment_id': 156}, {'id': 158, 'content': '不错。继续加油', 'farther_comment_id': None}, {'id': 159, 'content': '运往直前', 'farther_comment_id': None}, {'id': 160, 'content': '加油啦', 'farther_comment_id': 159}, {'id': 161, 'content': '爱你啊', 'farther_comment_id': 159}, {'id': 162, 'content': '@undefined\n的说的得分是', 'farther_comment_id': None} ]
一开始查到的所有的评论是这样的列表,我们需要把这些数据构建一下,方便处理
# ===========放一个字典,吧id提出来,并且在每一个字典中加一个"children_comment":[]键值对========== comment_dict = {} for comment in comment_list: comment["children_comment"] = [] comment_dict[comment["id"]]=comment # print(comment_dict) #{1:{"":"","":""},2:{"":"","":""},3:{}} { 154: {'id': 154, 'content': '发个', 'farther_comment_id': None, 'children_comment': []}, 155: {'id': 155, 'content': '你好啊', 'farther_comment_id': None, 'children_comment': []}, 156: {'id': 156, 'content': '嘻嘻', 'farther_comment_id': 155, 'children_comment': []}, 157: {'id': 157, 'content': '哎呦', 'farther_comment_id': 156, 'children_comment': []}, 158: {'id': 158, 'content': '不错。继续加油', 'farther_comment_id': None, 'children_comment': []}, 159: {'id': 159, 'content': '运往直前', 'farther_comment_id': None, 'children_comment': []}, 160: {'id': 160, 'content': '加油啦', 'farther_comment_id': 159, 'children_comment': []}, 161: {'id': 161, 'content': '爱你啊', 'farther_comment_id': 159, 'children_comment': []}, 162: {'id': 162, 'content': '@undefined\n的说的得分是', 'farther_comment_id': None, 'children_comment': []} }
找farther_comment_id为none的存放起来
comment_tree=[] for comment in comment_list: pid = comment["farther_comment_id"] if pid: #如果有值就找到对应的pid添加到children_comment列表中 comment_dict[pid]["children_comment"].append(comment) else: '''如果pid为none的时候找一个列表存起来''' comment_tree.append(comment) print(comment_tree)
最后拿到的comment_tree是这样的
''' comment_tree: [ { 'id': 154, 'content': '发个', 'farther_comment_id': None, 'children_comment': [ ] }, { 'id': 155, 'content': '你好啊', 'farther_comment_id': None, 'children_comment': [ { 'id': 156, 'content': '嘻嘻', 'farther_comment_id': 155, 'children_comment': [ { 'id': 157, 'content': '哎呦', 'farther_comment_id': 156, 'children_comment': [ ] } ] } ] }, { 'id': 158, 'content': '不错。继续加油', 'farther_comment_id': None, 'children_comment': [] }, { 'id': 159, 'content': '运往直前', 'farther_comment_id': None, 'children_comment': [ { 'id': 160, 'content': '加油啦', 'farther_comment_id': 159, 'children_comment': [ ] }, { 'id': 161, 'content': '爱你啊', 'farther_comment_id': 159, 'children_comment': [ ] } ] }, { 'id': 162, 'content': '@undefined\n的说的得分是', 'farther_comment_id': None, 'children_comment': [ ] } ] '''
2、===============获取评论树=================
在后端吧上面结构化的数据返回过去, 在前端发ajax请求: $.ajax({ url:"/blog/commentTree/{{article_obj.id}}", type:"post", success:function(data){ console.log(data) var data = JSON.parse(data) //定义一个展开评论树的函数 var s = showCommentTree(data) $(".comment_tree_list").append(s) } })
3、===============展开评论树=================
展开应该是这样的层级
2 2222 22222 3 33 4 模拟上面的层级结构 <div class="comment_tree_list"> <div class="comment_list"> <div class="content_t"><span>2</span></div> <div class="comment_list"> <div class="content_t"><span>2222</span></div> <div class="comment_list"> <div class="content_t"><span>22222</span></div> </div> </div> </div> <div class="comment_list"> <div class="content"><span>3</span></div> </div> </div> </div>
通过下面的函数构建像上面这样的结构
function showCommentTree(comment_list){ //comment_list传进来的那个data[{},{},{}] var html = '' $.each(comment_list,function(i,comment_dict){ //comment_dict是一个一个的字典 var val = comment_dict["content"] var content_str = '<div class="comment_list"><div class="content"><span>+val+</span></div>'
if(comment_dict["chidren_commentList"]){
var s=showCommentTree(comment_dict["chidren_commentList"]); // [{},{}]
commnent_str+=s
}
content_str+="</div>"吧最后的div拿走,拼接一下(因为有时候你不确定它的中间还有没有自评论,不要给写死了)
html +=content_str })
return html #返回的值让s接收了
}
错开他们的位置:找到content_t标签,margin-left
递归函数的两个条件:
1、自己调用自己,,,,
2、有一个结束条件。,,
四、关于评论回复点赞按钮是不是要求必须用户是登陆状态
客户端如何判断是否登录: 方法1:设置自定义的属性 <div class="info" user_username="{{ request.user.username }}" article_id="{{ article_obj.nid }}"></div> if ($(".info").attr("user_username")){// 登录成功状态} 方法2:在服务端设置cookie obj=render(request,"article_detail.html",locals()) obj.set_cookie("user_username",request.user.username) return obj if($.cookie("user_username")){// 登录成功状态}
这两种方式也有好处:
当我们在js文件在外部引用的时候,只要是有模板语法的都是不被渲染的。(因为当render返回页面的时候,会渲染整个页面,当它吧整个页面渲染完的时候,才发现有个<script src="xxx.js"></script>),这时候当在去请求这个路径的时候就不会渲染了,因为已经渲染完成了
所以我们可以采用上面的方式,吧模板语法替换了
五、跳转问题:从哪来跳那去( window.location.href)
当你打开文章要看的时候,你进行点赞跳转到登录,当用户登录进来的时候应该看到的也是当前的文章页面
思路:获取上一次的url路径
有三种方式:
一:根据location.pathname:
location.pathname :返回的是当前url的路径部分
article_detail.html: if 未登录:location.href="/login/?next="+location.pathname // /login/?next=/blog/yuan/articles/1 login.html: if (response["is_login"]){ if(location.search.slice(6)){ // /blog/yuan/articles/1 //location.search只拿问号后面的,包括问号 #如果拿到的是当前你点击的文章的那个路径就还是让跳转的这个位置 location.href=location.search.slice(6) } else { location.href="/" } }
二: 根据(document.referrer)
referrer是上一次请求的路径 article_detail.html: if 未登录:location.href="/login/" login.html: if (response["is_login"]){ if(document.referrer){ // /blog/yuan/articles/1 如果是上一次请求的路径,就额昂跳转到上一次请求的路径 location.href=document.referrer } else { location.href="/" } }
三:根据cookie设置next_path(推荐用这种)
views.py : def articleDetail(request) obj=render(request,"article_detail.html",locals()) obj.set_cookie("next_path",request.path) return obj article_detail.html: if 未登录:location.href="/login/" login.html: if (response["is_login"]){ $.cookie("next_path") if($.cookie("next_path")){ // /blog/yuan/articles/1 location.href=$.cookie("next_path") } else { location.href="/" } }
五、计算园龄的时间
计算园龄的时间:用先在的时间减去创建的时间
由于时间没有减法的过滤器,我们可以自定义一个过滤器
1、创建一个templatetags文件夹------文件夹下创建myfilter.py
2、需要导入
from django import template
import datetime
from django.utils.safestring import mark_safe
3、开始写计算时间差的函数
register = template.Library() # register的名字是固定的,不可改变 @register.filter #过滤器 def yuanlingtime(createtime): #计算时间差:当前的时间减去创建的时间 now_time = datetime.datetime.now() #当前的时间 user_create_time = datetime.datetime(year=createtime.year,month=createtime.month,day=createtime.day,hour=createtime.hour,minute=createtime.minute,second=createtime.second) ret = now_time-user_create_time print("---------",ret) #5 days, 15:51:47.855688 print(type(ret)) #<class 'datetime.timedelta'> return mark_safe(str(ret)[:-17]) #mark_dafe只是做一个安全机制,和safe过滤器一样,安全之后返回 # print(datetime.datetime(year=2017,month=2,day=5)) #2017-02-05 00:00:00
4、在模板中调用过滤器
先导入:
{% load myfilter %}
<p>园龄:{{ current_user.create_time|yuanlingtime }}</p>