python 全栈开发,Day82(点赞和踩灭,用户评论)
一、點(diǎn)贊和踩滅
樣式
先來(lái)做樣式,修改article_detail.html,增加div_digg的div
{% extends "base.html" %}{% block content %}<div class="article_info"><h4 class="text-center">{{ article_obj.title }}</h4><div class="content">{{ article_obj.content|safe }}</div><div id="div_digg"><div class="diggit"><span class="diggnum" id="digg_count">{{ article_obj.up_count }}</span></div><div class="buryit"><span class="burynum" id="bury_count">{{ article_obj.down_count }}</span></div><div class="clear"></div><div class="diggword" id="digg_tips"></div></div></div>{% endblock %} View Code在static-->css目錄下,創(chuàng)建文件article_detail.css
#div_digg { float: right;margin-bottom: 10px;margin-right: 30px;font-size: 12px;width: 125px;text-align: center;margin-top: 10px; }.diggit {float: left;width: 46px;height: 52px;background: url("/static/img/upup.gif") no-repeat;text-align: center;cursor: pointer;margin-top: 2px;padding-top: 5px; }.buryit {float: right;margin-left: 20px;width: 46px;height: 52px;background: url("/static/img/downdown.gif") no-repeat;text-align: center;cursor: pointer;margin-top: 2px;padding-top: 5px; } View Code從博客園拷貝2個(gè)圖片到static-->img目錄下
修改base.html,引入article_detail.css
<!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8"><title>Title</title><link rel="shortcut icon" href="https://common.cnblogs.com/favicon.ico" type="image/x-icon"/>{#公共樣式#}<link rel="stylesheet" href="/static/css/theme/common.css">{#個(gè)人站點(diǎn)主題樣式#}<link rel="stylesheet" href="/static/css/theme/{{ blog.theme }}">{#bootstrap#}<link rel="stylesheet" href="/static/bootstrap/css/bootstrap.css"><script src="/static/js/jquery.js"></script><script src="/static/bootstrap/js/bootstrap.js"></script>{#文章詳情#}<link rel="stylesheet" href="/static/css/article_detail.css"></head> <body> <div class="header"><p class="title">{{ blog.title }}</p> </div><div class="container-fluid"><div class="row"><div class="col-md-3">{#加載自定義標(biāo)簽?zāi)K#}{% load my_tags %}{#調(diào)用get_query_data標(biāo)簽,它返回left_region.html,是已經(jīng)被渲染過(guò)的文件#}{% get_query_data username %}</div><div class="col-md-9">{% block content %}{% endblock %}</div></div> </div></body> </html> View Code點(diǎn)擊一篇文章:http://127.0.0.1:8000/xiao/articles/5/
拉到最下面,右側(cè)效果如下:
?
綁定事件
推薦和反對(duì)有2個(gè)按鈕,要綁定2個(gè)事件嗎?答案是可以,但是不推薦使用!為什么呢?
因?yàn)闀?huì)造成大量代碼重復(fù)!這2個(gè)按鈕需要發(fā)送的文章id和用戶id是一樣的,唯一不同的就是:一個(gè)是推薦,一個(gè)是反對(duì)。
修改article_detail.html增加div,測(cè)試js代碼
注意:在base.html中已經(jīng)導(dǎo)入了jquery
hasClass() 方法檢查被選元素是否包含指定的 class。它返回True和False
{% extends "base.html" %}{% block content %}<div class="article_info"><h4 class="text-center">{{ article_obj.title }}</h4><div class="content">{{ article_obj.content|safe }}</div><div id="div_digg"><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"></div></div></div><script>$(".action").click(function () {{#hasClass() 方法檢查被選元素是否包含指定的 class#}var is_up = $(this).hasClass("diggit");alert(is_up);})</script>{% endblock %} View Code刷新網(wǎng)頁(yè),點(diǎn)擊文章右下角的推薦,提示true
點(diǎn)擊反對(duì),提示false
?
發(fā)送ajax請(qǐng)求
數(shù)據(jù)分析
到底是用get還是post呢?有一個(gè)規(guī)范:查詢用get,更改用post
查看blog_articleupdown表,這個(gè)是點(diǎn)贊表。插入一條記錄,需要3個(gè)參數(shù),分別是是否點(diǎn)贊、文章id、用戶id
注意:這個(gè)用戶id是當(dāng)前登錄用戶,不是文章作者!
由于在視圖函數(shù)中,有一個(gè)全局變量request.user。它是用戶登錄之后,才有的!所以這個(gè)用戶id,不需要傳。
只需要傳2個(gè)值就可以了,分別是是否點(diǎn)贊和文章id。
在article_detail.html中,已經(jīng)有一個(gè)article_obj,它是一個(gè)model對(duì)象,那么就可以得到文章id。
是否點(diǎn)贊,通過(guò)js代碼中的is_up變量,也可以知道。
所以發(fā)送的數(shù)據(jù),不需要使用標(biāo)簽選擇器來(lái)獲取。直接使用即可!
操作流程
服務(wù)器處理
修改urls.py,增加一個(gè)路徑digg
urlpatterns = [path('admin/', admin.site.urls),path('login/', views.login),path('index/', views.index),path('logout/', views.logout),path('', views.index),#點(diǎn)贊或者踩滅path('digg/', views.digg),#文章詳情re_path('(?P<username>\w+)/articles/(?P<article_id>\d+)/$', views.article_detail),# 跳轉(zhuǎn)re_path('(?P<username>\w+)/(?P<condition>category|tag|achrive)/(?P<params>.*)/$', views.homesite),# 個(gè)人站點(diǎn)re_path('(?P<username>\w+)/$', views.homesite), ] View Code注意:digg要放到文章詳情的上面
修改article_detail.html
{% extends "base.html" %}{% block content %}<div class="article_info"><h4 class="text-center">{{ article_obj.title }}</h4><div class="content">{{ article_obj.content|safe }}</div><div id="div_digg"><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"></div></div></div>{% csrf_token %}<script>$(".action").click(function () {{#hasClass() 方法檢查被選元素是否包含指定的 class#}var is_up = $(this).hasClass("diggit");{#判斷是否登錄#}if ("{{ request.user.username }}") {$.ajax({url: "/digg/",type: "post",data: {is_up: is_up,article_id: "{{ article_obj.pk }}",csrfmiddlewaretoken: $("[name='csrfmiddlewaretoken']").val()},success: function (data) {console.log(data);console.log(typeof data);}})} else {location.href = "/login/";}})</script>{% endblock %} View Code修改views.py,增加視圖函數(shù)digg
注意:導(dǎo)入2個(gè)模塊
import json from django.http import JsonResponse視圖函數(shù)如下:
from django.shortcuts import render,HttpResponse,redirect from django.contrib import auth from blog.models import Article,UserInfo,Blog,Category,Tag,ArticleUpDown from django.db.models import Sum,Avg,Max,Min,Count from django.db.models import F import json from django.http import JsonResponse# Create your views here. def login(request):if request.method=="POST":user=request.POST.get("user")pwd=request.POST.get("pwd")# 用戶驗(yàn)證成功,返回user對(duì)象,否則返回Noneuser=auth.authenticate(username=user,password=pwd)if user:# 登錄,注冊(cè)session# 全局變量 request.user=當(dāng)前登陸對(duì)象(session中) auth.login(request,user)return redirect("/index/")return render(request,"login.html")def index(request):article_list=Article.objects.all()return render(request,"index.html",{"article_list":article_list})def logout(request): # 注銷 auth.logout(request)return redirect("/index/")def query_current_site(request,username): # 查詢當(dāng)前站點(diǎn)的博客標(biāo)題# 查詢當(dāng)前站點(diǎn)的用戶對(duì)象user = UserInfo.objects.filter(username=username).first()if not user:return render(request, "not_found.html")# 查詢當(dāng)前站點(diǎn)對(duì)象blog = user.blogreturn blogdef homesite(request,username,**kwargs): # 個(gè)人站點(diǎn)主頁(yè)print("kwargs", kwargs)blog = query_current_site(request,username)# 查詢當(dāng)前用戶發(fā)布的所有文章if not kwargs:article_list = Article.objects.filter(user__username=username)else:condition = kwargs.get("condition")params = kwargs.get("params")#判斷分類、隨筆、歸檔if condition == "category":article_list = Article.objects.filter(user__username=username).filter(category__title=params)elif condition == "tag":article_list = Article.objects.filter(user__username=username).filter(tags__title=params)else:year, month = params.split("/")article_list = Article.objects.filter(user__username=username).filter(create_time__year=year,create_time__month=month)return render(request,"homesite.html",{"blog":blog,"username":username,"article_list":article_list})def article_detail(request,username,article_id):blog = query_current_site(request,username)#查詢指定id的文章article_obj = Article.objects.filter(pk=article_id).first()user_id = UserInfo.objects.filter(username=username).first().nidreturn render(request,'article_detail.html',{"blog":blog,"username":username,'article_obj':article_obj,"user_id":user_id})def digg(request):print(request.POST)if request.method == "POST":#ajax發(fā)送的過(guò)來(lái)的true和false是字符串,使用json反序列化得到布爾值is_up = json.loads(request.POST.get("is_up"))article_id = request.POST.get("article_id")user_id = request.user.pkresponse = {"state": True, "msg": None} # 初始狀態(tài)#判斷當(dāng)前登錄用戶是否對(duì)這篇文章做過(guò)點(diǎn)贊或者踩滅操作obj = ArticleUpDown.objects.filter(user_id=user_id, article_id=article_id).first()if obj:response["state"] = False # 更改狀態(tài)else:#插入一條記錄new_obj = ArticleUpDown.objects.create(user_id=user_id, article_id=article_id, is_up=is_up)return JsonResponse(response)else:return HttpResponse("非法請(qǐng)求") View Codejson處理問(wèn)題
ajax發(fā)送的過(guò)來(lái)的true和false是字符串,使用json反序列化得到布爾值
json支持7種數(shù)據(jù)格式,其中true和false是其中2種。那么反序列化之后,會(huì)得到一個(gè)布爾值。
為什么一定要反序列化呢?因?yàn)?..create(...is_up=is_up) 等式右邊的值,非空。那么ORM會(huì)認(rèn)為是True!
所以不管ajax傳過(guò)來(lái)的是true和falase,它都是字符串,那么ORM執(zhí)行時(shí),始終都是True
JsonResponse
JsonResponse對(duì)象是HttpRespon的子類,它主要和父類的區(qū)別在于:
1.它的默認(rèn)Content-Type 被設(shè)置為: application/json
2.第一個(gè)參數(shù),data應(yīng)該是一個(gè)字典類型,如果不是字典,拋出 TypeError的異常
它返回json數(shù)據(jù),那么ajax接收時(shí),不需要反序列化,可以直接使用!
?
刷新網(wǎng)頁(yè),點(diǎn)擊右側(cè)的推薦,查看瀏覽器控制臺(tái)
查看blog_articleupdown表記錄,發(fā)現(xiàn)多了一條記錄
查看Pycharm控制臺(tái)輸出:
<QueryDict: {'article_id': ['6'], 'csrfmiddlewaretoken': ['JgLyFpVgp92Rs8ppPCd2pm9jVj6z8bo9KSsMwKnakpB6CwTCT1K58v2JHLeR5ejN'], 'is_up': ['true']}>
?
判斷用戶之前的操作
當(dāng)用戶第一次點(diǎn)擊推薦時(shí),直接將數(shù)字加1。再次點(diǎn)擊時(shí),提示您已經(jīng)推薦過(guò)!再次點(diǎn)擊返回時(shí),也提示您已經(jīng)推薦過(guò)。
同理,當(dāng)用戶對(duì)一篇點(diǎn)擊返回后。之后不論是點(diǎn)擊推薦還是反對(duì),都會(huì)提示您已經(jīng)反對(duì)過(guò)!
不可以取消推薦或者反對(duì)!
修改views.py,獲取上一次操作的記錄
def digg(request):print(request.POST)if request.method == "POST":#ajax發(fā)送的過(guò)來(lái)的true和false是字符串,使用json反序列化得到布爾值is_up = json.loads(request.POST.get("is_up"))article_id = request.POST.get("article_id")user_id = request.user.pkresponse = {"state": True, "msg": None} # 初始狀態(tài)#判斷當(dāng)前登錄用戶是否對(duì)這篇文章做過(guò)點(diǎn)贊或者踩滅操作obj = ArticleUpDown.objects.filter(user_id=user_id, article_id=article_id).first()if obj:response["state"] = False # 更改狀態(tài)response["handled"] = obj.is_up # 獲取之前的操作,返回true或者falseprint(obj.is_up)else:#插入一條記錄new_obj = ArticleUpDown.objects.create(user_id=user_id, article_id=article_id, is_up=is_up)return JsonResponse(response)else:return HttpResponse("非法請(qǐng)求") View Code修改article_detail.html的js代碼
{% extends "base.html" %}{% block content %}<div class="article_info"><h4 class="text-center">{{ article_obj.title }}</h4><div class="content">{{ article_obj.content|safe }}</div><div id="div_digg"><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"></div></div></div>{% csrf_token %}<script>$(".action").click(function () {{#hasClass() 方法檢查被選元素是否包含指定的 class#}var is_up = $(this).hasClass("diggit");{#判斷是否登錄#}if ("{{ request.user.username }}") {$.ajax({url: "/digg/",type: "post",data: {is_up: is_up,article_id: "{{ article_obj.pk }}",csrfmiddlewaretoken: $("[name='csrfmiddlewaretoken']").val()},success: function (data) {console.log(data);console.log(typeof data);if (data.state) {//提交成功} else {if (data.handled) { //判斷之前的操作記錄$("#digg_tips").html("您已經(jīng)推薦過(guò)!")} else {$("#digg_tips").html("您已經(jīng)反對(duì)過(guò)!")}}}})} else {location.href = "/login/";}})</script>{% endblock %} View Code修改static-->css目錄下的article_detail.css,增加樣式
#div_digg { float: right;margin-bottom: 10px;margin-right: 30px;font-size: 12px;width: 125px;text-align: center;margin-top: 10px; }.diggit {float: left;width: 46px;height: 52px;background: url("/static/img/upup.gif") no-repeat;text-align: center;cursor: pointer;margin-top: 2px;padding-top: 5px; }.buryit {float: right;margin-left: 20px;width: 46px;height: 52px;background: url("/static/img/downdown.gif") no-repeat;text-align: center;cursor: pointer;margin-top: 2px;padding-top: 5px; } .clear {clear: both; }#digg_tips{ color: red; } View Code重啟django項(xiàng)目,頁(yè)面強(qiáng)制刷新幾次!
再次點(diǎn)擊,提示已經(jīng)點(diǎn)過(guò)了
修改article_detail.html的js代碼,增加動(dòng)態(tài)效果,1秒后,清空紅色文字
{% extends "base.html" %}{% block content %}<div class="article_info"><h4 class="text-center">{{ article_obj.title }}</h4><div class="content">{{ article_obj.content|safe }}</div><div id="div_digg"><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"></div></div></div>{% csrf_token %}<script>$(".action").click(function () {{#hasClass() 方法檢查被選元素是否包含指定的 class#}var is_up = $(this).hasClass("diggit");{#判斷是否登錄#}if ("{{ request.user.username }}") {$.ajax({url: "/digg/",type: "post",data: {is_up: is_up,article_id: "{{ article_obj.pk }}",csrfmiddlewaretoken: $("[name='csrfmiddlewaretoken']").val()},success: function (data) {console.log(data);console.log(typeof data);if (data.state) {//提交成功} else {if (data.handled) { //判斷之前的操作記錄$("#digg_tips").html("您已經(jīng)推薦過(guò)!")} else {$("#digg_tips").html("您已經(jīng)反對(duì)過(guò)!")}setTimeout(function () {$("#digg_tips").html("") //清空提示文字}, 1000)}}})} else {location.href = "/login/";}})</script>{% endblock %} View Code效果如下:
?
更新文章表
blog_articleupdown表有一個(gè)聯(lián)合唯一索引,即使重復(fù)點(diǎn)擊,也不會(huì)增加記錄!
blog_article表有一個(gè)up_count和down_count字段,分別表示推薦和反對(duì)的計(jì)數(shù)。
那么這2個(gè)字段,也需要同時(shí)更新。在原有數(shù)值的基礎(chǔ)上加1,需要使用F查詢!
導(dǎo)入F模塊,完整代碼如下:
def digg(request):print(request.POST)if request.method == "POST":#ajax發(fā)送的過(guò)來(lái)的true和false是字符串,使用json反序列化得到布爾值is_up = json.loads(request.POST.get("is_up"))article_id = request.POST.get("article_id")user_id = request.user.pkresponse = {"state": True, "msg": None} # 初始狀態(tài)#判斷當(dāng)前登錄用戶是否對(duì)這篇文章做過(guò)點(diǎn)贊或者踩滅操作obj = ArticleUpDown.objects.filter(user_id=user_id, article_id=article_id).first()if obj:response["state"] = False # 更改狀態(tài)response["handled"] = obj.is_up # 獲取之前的操作,返回true或者falseprint(obj.is_up)else:#插入一條記錄new_obj = ArticleUpDown.objects.create(user_id=user_id, article_id=article_id, is_up=is_up)if is_up: # 判斷為推薦Article.objects.filter(pk=article_id).update(up_count=F("up_count")+1)else: # 反對(duì)Article.objects.filter(pk=article_id).update(down_count=F("down_count")+1)return JsonResponse(response)else:return HttpResponse("非法請(qǐng)求") View Code清空blog_articleupdown表記錄
訪問(wèn)網(wǎng)頁(yè),重新點(diǎn)擊。再次刷新頁(yè)面,那么值就會(huì)更新了!
?
網(wǎng)頁(yè)實(shí)時(shí)展示
上面已經(jīng)實(shí)現(xiàn)了,點(diǎn)擊之后,更新數(shù)據(jù)庫(kù)。但是需要刷新網(wǎng)頁(yè),才會(huì)有效果。想要用戶能實(shí)時(shí)看到數(shù)字,需要進(jìn)行DOM操作。
獲取網(wǎng)頁(yè)原有的值,進(jìn)行加1。最后進(jìn)行DOM操作,展示給用戶看!
修改article_detail.html的js代碼
注意:js是弱類型語(yǔ)言,獲取到值時(shí),不能直接進(jìn)行加法,需要強(qiáng)制轉(zhuǎn)換才行!
{% extends "base.html" %}{% block content %}<div class="article_info"><h4 class="text-center">{{ article_obj.title }}</h4><div class="content">{{ article_obj.content|safe }}</div><div id="div_digg"><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"></div></div></div>{% csrf_token %}<script>$(".action").click(function () {{#hasClass() 方法檢查被選元素是否包含指定的 class#}var is_up = $(this).hasClass("diggit");{#獲取提示的span標(biāo)簽#}var _this= $(this).children("span");{#判斷是否登錄#}if ("{{ request.user.username }}") {$.ajax({url: "/digg/",type: "post",data: {is_up: is_up,article_id: "{{ article_obj.pk }}",csrfmiddlewaretoken: $("[name='csrfmiddlewaretoken']").val()},success: function (data) {console.log(data);console.log(typeof data);if (data.state) {//提交成功var val=_this.text(); //獲取text值//在原有的基礎(chǔ)上加1。注意:一定要進(jìn)行類型轉(zhuǎn)換_this.text(parseInt(val)+1)} else {if (data.handled) { //判斷之前的操作記錄$("#digg_tips").html("您已經(jīng)推薦過(guò)!")} else {$("#digg_tips").html("您已經(jīng)反對(duì)過(guò)!")}setTimeout(function () {$("#digg_tips").html("") //清空提示文字}, 1000)}}})} else {location.href = "/login/";}})</script>{% endblock %} View Code清空blog_articleupdown表記錄
將blog_article表的相關(guān)字段設(shè)置為0
訪問(wèn)網(wǎng)頁(yè),重新點(diǎn)擊,效果如下:
優(yōu)化js代碼
將if判斷部分,改成三元運(yùn)算
{% extends "base.html" %}{% block content %}<div class="article_info"><h4 class="text-center">{{ article_obj.title }}</h4><div class="content">{{ article_obj.content|safe }}</div><div id="div_digg"><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"></div></div></div>{% csrf_token %}<script>$(".action").click(function () {{#hasClass() 方法檢查被選元素是否包含指定的 class#}var is_up = $(this).hasClass("diggit");{#獲取提示的span標(biāo)簽#}var _this = $(this).children("span");{#判斷是否登錄#}if ("{{ request.user.username }}") {$.ajax({url: "/digg/",type: "post",data: {is_up: is_up,article_id: "{{ article_obj.pk }}",csrfmiddlewaretoken: $("[name='csrfmiddlewaretoken']").val()},success: function (data) {console.log(data);console.log(typeof data);if (data.state) {//提交成功var val = _this.text(); //獲取text值//在原有的基礎(chǔ)上加1。注意:一定要進(jìn)行類型轉(zhuǎn)換_this.text(parseInt(val) + 1)} else {// 重復(fù)提交var val = data.handled ? "您已經(jīng)推薦過(guò)!" : "您已經(jīng)反對(duì)過(guò)!";$("#digg_tips").html(val);setTimeout(function () {$("#digg_tips").html("") //清空提示文字}, 1000)}}})} else {location.href = "/login/";}})</script>{% endblock %} View Code?
事務(wù)
是指作為單個(gè)邏輯工作單元執(zhí)行的一系列操作,要么完全地執(zhí)行,要么完全地不執(zhí)行。 事務(wù)處理可以確保除非事務(wù)性單元內(nèi)的所有操作都成功完成,否則不會(huì)永久更新面向數(shù)據(jù)的資源。
在上面的例子,點(diǎn)贊時(shí),需要更新2個(gè)表。一個(gè)是blog_articleupdown表,一個(gè)是blog_article表。
如果有一個(gè)表沒有更新,那么就會(huì)產(chǎn)生臟數(shù)據(jù)!為了避免這個(gè)問(wèn)題,需要使用事務(wù)!
舉例:模擬異常情況
修改digg視圖函數(shù),模擬異常
def digg(request):print(request.POST)if request.method == "POST":#ajax發(fā)送的過(guò)來(lái)的true和false是字符串,使用json反序列化得到布爾值is_up = json.loads(request.POST.get("is_up"))article_id = request.POST.get("article_id")user_id = request.user.pkresponse = {"state": True, "msg": None} # 初始狀態(tài)#判斷當(dāng)前登錄用戶是否對(duì)這篇文章做過(guò)點(diǎn)贊或者踩滅操作obj = ArticleUpDown.objects.filter(user_id=user_id, article_id=article_id).first()if obj:response["state"] = False # 更改狀態(tài)response["handled"] = obj.is_up # 獲取之前的操作,返回true或者falseprint(obj.is_up)else:#插入一條記錄new_obj = ArticleUpDown.objects.create(user_id=user_id, article_id=article_id, is_up=is_up)ask # 這一行故意出錯(cuò)if is_up: # 判斷為推薦Article.objects.filter(pk=article_id).update(up_count=F("up_count")+1)else: # 反對(duì)Article.objects.filter(pk=article_id).update(down_count=F("down_count")+1)return JsonResponse(response)else:return HttpResponse("非法請(qǐng)求") View Code清空blog_articleupdown表,將blog_article表的up_count和down_count字段設(shè)置為0
刷新頁(yè)面,重新點(diǎn)擊,打開瀏覽器工具-->network
出行了500錯(cuò)誤
?查看blog_articleupdown表,多了一條記錄
?
查看blog_article表,對(duì)應(yīng)文章的up_count字段,發(fā)現(xiàn)還是為0
?
使用事務(wù)
修改digg視圖函數(shù),導(dǎo)入模塊transaction。
使用語(yǔ)法很簡(jiǎn)單,將相關(guān)原子操作的代碼直接tab一下,就可以了
完整代碼如下:
from django.shortcuts import render,HttpResponse,redirect from django.contrib import auth from blog.models import Article,UserInfo,Blog,Category,Tag,ArticleUpDown from django.db.models import Sum,Avg,Max,Min,Count from django.db.models import F import json from django.http import JsonResponse from django.db import transaction# Create your views here. def login(request):if request.method=="POST":user=request.POST.get("user")pwd=request.POST.get("pwd")# 用戶驗(yàn)證成功,返回user對(duì)象,否則返回Noneuser=auth.authenticate(username=user,password=pwd)if user:# 登錄,注冊(cè)session# 全局變量 request.user=當(dāng)前登陸對(duì)象(session中) auth.login(request,user)return redirect("/index/")return render(request,"login.html")def index(request):article_list=Article.objects.all()return render(request,"index.html",{"article_list":article_list})def logout(request): # 注銷 auth.logout(request)return redirect("/index/")def query_current_site(request,username): # 查詢當(dāng)前站點(diǎn)的博客標(biāo)題# 查詢當(dāng)前站點(diǎn)的用戶對(duì)象user = UserInfo.objects.filter(username=username).first()if not user:return render(request, "not_found.html")# 查詢當(dāng)前站點(diǎn)對(duì)象blog = user.blogreturn blogdef homesite(request,username,**kwargs): # 個(gè)人站點(diǎn)主頁(yè)print("kwargs", kwargs)blog = query_current_site(request,username)# 查詢當(dāng)前用戶發(fā)布的所有文章if not kwargs:article_list = Article.objects.filter(user__username=username)else:condition = kwargs.get("condition")params = kwargs.get("params")#判斷分類、隨筆、歸檔if condition == "category":article_list = Article.objects.filter(user__username=username).filter(category__title=params)elif condition == "tag":article_list = Article.objects.filter(user__username=username).filter(tags__title=params)else:year, month = params.split("/")article_list = Article.objects.filter(user__username=username).filter(create_time__year=year,create_time__month=month)return render(request,"homesite.html",{"blog":blog,"username":username,"article_list":article_list})def article_detail(request,username,article_id):blog = query_current_site(request,username)#查詢指定id的文章article_obj = Article.objects.filter(pk=article_id).first()user_id = UserInfo.objects.filter(username=username).first().nidreturn render(request,'article_detail.html',{"blog":blog,"username":username,'article_obj':article_obj,"user_id":user_id})def digg(request):print(request.POST)if request.method == "POST":#ajax發(fā)送的過(guò)來(lái)的true和false是字符串,使用json反序列化得到布爾值is_up = json.loads(request.POST.get("is_up"))article_id = request.POST.get("article_id")user_id = request.user.pkresponse = {"state": True, "msg": None} # 初始狀態(tài)#判斷當(dāng)前登錄用戶是否對(duì)這篇文章做過(guò)點(diǎn)贊或者踩滅操作obj = ArticleUpDown.objects.filter(user_id=user_id, article_id=article_id).first()if obj:response["state"] = False # 更改狀態(tài)response["handled"] = obj.is_up # 獲取之前的操作,返回true或者falseprint(obj.is_up)else:with transaction.atomic():#插入一條記錄new_obj = ArticleUpDown.objects.create(user_id=user_id, article_id=article_id, is_up=is_up)ask # 模擬異常if is_up: # 判斷為推薦Article.objects.filter(pk=article_id).update(up_count=F("up_count")+1)else: # 反對(duì)Article.objects.filter(pk=article_id).update(down_count=F("down_count")+1)return JsonResponse(response)else:return HttpResponse("非法請(qǐng)求") View Code清空blog_articleupdown表
刷新頁(yè)面,重新點(diǎn)擊,再一次返回500錯(cuò)誤
查看blog_articleupdown表,發(fā)現(xiàn)并沒增加一條記錄。
?
這樣就比較合理了!刪除異常代碼
def digg(request):print(request.POST)if request.method == "POST":#ajax發(fā)送的過(guò)來(lái)的true和false是字符串,使用json反序列化得到布爾值is_up = json.loads(request.POST.get("is_up"))article_id = request.POST.get("article_id")user_id = request.user.pkresponse = {"state": True, "msg": None} # 初始狀態(tài)#判斷當(dāng)前登錄用戶是否對(duì)這篇文章做過(guò)點(diǎn)贊或者踩滅操作obj = ArticleUpDown.objects.filter(user_id=user_id, article_id=article_id).first()if obj:response["state"] = False # 更改狀態(tài)response["handled"] = obj.is_up # 獲取之前的操作,返回true或者falseprint(obj.is_up)else:with transaction.atomic():#插入一條記錄new_obj = ArticleUpDown.objects.create(user_id=user_id, article_id=article_id, is_up=is_up)if is_up: # 判斷為推薦Article.objects.filter(pk=article_id).update(up_count=F("up_count")+1)else: # 反對(duì)Article.objects.filter(pk=article_id).update(down_count=F("down_count")+1)return JsonResponse(response)else:return HttpResponse("非法請(qǐng)求") View Code刷新頁(yè)面,重新點(diǎn)擊
查看blog_articleupdown表,發(fā)現(xiàn)增加了一條記錄。
查看blog_article表,對(duì)應(yīng)文章的up_count字段,發(fā)現(xiàn)為1了!
事務(wù)用起來(lái)很簡(jiǎn)單,是因?yàn)閐jango把復(fù)雜的邏輯給封裝好了!
有時(shí)間的小伙伴,還可以加一個(gè)需求,自己不能給自己點(diǎn)贊!
?
二、用戶評(píng)論
評(píng)論樣式
先寫html的樣式代碼
修改article_detail.html
{% extends "base.html" %}{% block content %}<div class="article_info"><h4 class="text-center">{{ article_obj.title }}</h4><div class="content">{{ article_obj.content|safe }}</div><div id="div_digg"><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"></div></div><div class="clearfix"></div><div class="comment"><p>評(píng)論列表</p><ul class="comment_list list-group">{% for comment in comment_list %}<li class="list-group-item"><div><a href="">#{{ forloop.counter }}樓</a> <span class="small">{{ comment.create_time|date:"Y-m-d H:i" }}</span> <a href="">{{ comment.user.username }}</a><a href="" class="pull-right"><span>回復(fù)</span></a></div><div><p>{{ comment.content }}</p></div></li>{% endfor %}</ul><p>發(fā)表評(píng)論</p><p>昵稱:<input type="text" id="tbCommentAuthor" class="author" disabled="disabled" size="50" value="{{ request.user.username }}"></p><div><textarea name="" id="comment_content" cols="60" rows="10"></textarea></div><input type="button" value="submit" class="btn btn-default comment_btn"></div></div>{% csrf_token %}<script>$(".action").click(function () {{#hasClass() 方法檢查被選元素是否包含指定的 class#}var is_up = $(this).hasClass("diggit");{#獲取提示的span標(biāo)簽#}var _this = $(this).children("span");{#判斷是否登錄#}if ("{{ request.user.username }}") {$.ajax({url: "/digg/",type: "post",data: {is_up: is_up,article_id: "{{ article_obj.pk }}",csrfmiddlewaretoken: $("[name='csrfmiddlewaretoken']").val()},success: function (data) {console.log(data);console.log(typeof data);if (data.state) {//提交成功var val = _this.text(); //獲取text值//在原有的基礎(chǔ)上加1。注意:一定要進(jìn)行類型轉(zhuǎn)換_this.text(parseInt(val) + 1)} else {// 重復(fù)提交var val = data.handled ? "您已經(jīng)推薦過(guò)!" : "您已經(jīng)反對(duì)過(guò)!";$("#digg_tips").html(val);setTimeout(function () {$("#digg_tips").html("") //清空提示文字}, 1000)}}})} else {location.href = "/login/";}})</script>{% endblock %} View Code修改css目錄下的article_detail.css
注意放一個(gè)gif圖片到img目錄下
/*推薦和反對(duì)*/ #div_digg { float: right;margin-bottom: 10px;margin-right: 30px;font-size: 12px;width: 125px;text-align: center;margin-top: 10px; }.diggit {float: left;width: 46px;height: 52px;background: url("/static/img/upup.gif") no-repeat;text-align: center;cursor: pointer;margin-top: 2px;padding-top: 5px; }.buryit {float: right;margin-left: 20px;width: 46px;height: 52px;background: url("/static/img/downdown.gif") no-repeat;text-align: center;cursor: pointer;margin-top: 2px;padding-top: 5px; } .clear {clear: both; }#digg_tips{ color: red; }/*評(píng)論*/input.author{background-image: url("/static/img/icon_form.gif");background-repeat: no-repeat;border: 1px solid #ccc; padding: 4px 4px 4px 30px;width: 300px;font-size: 13px; }input.author {background-position: 3px -3px; } View Code刷新網(wǎng)頁(yè),拉到最下面,效果如下:
評(píng)論步驟
- 提交根評(píng)論請(qǐng)求
- 顯示根評(píng)論
- 提交子評(píng)論
- 顯示子評(píng)論
- 評(píng)論樹
?
發(fā)送ajax請(qǐng)求
查看評(píng)論表模型
class Comment(models.Model):"""評(píng)論表"""nid = models.AutoField(primary_key=True)article = models.ForeignKey(verbose_name='評(píng)論文章', to='Article', to_field='nid', on_delete=models.CASCADE)user = models.ForeignKey(verbose_name='評(píng)論者', to='UserInfo', to_field='nid', on_delete=models.CASCADE)content = models.CharField(verbose_name='評(píng)論內(nèi)容', max_length=255)create_time = models.DateTimeField(verbose_name='創(chuàng)建時(shí)間', auto_now_add=True)parent_comment = models.ForeignKey(verbose_name="父級(jí)評(píng)論id", to='Comment',null=True, on_delete=models.CASCADE) View Code它有6個(gè)字段,其中nid、user、parent_comment、create_time是可選的。
user?在視圖函數(shù)中,可以直接獲取,不需要ajax傳此參數(shù)。
parent_comment字段的值為空,表示這是一條根評(píng)論。否則為一條子評(píng)論!
那么ajax如果傳一個(gè)空值,表示為根評(píng)論,否則為子評(píng)論。
create_time字段有一個(gè)屬性:auto_now_add=True。
字段在實(shí)例第一次保存的時(shí)候會(huì)保存當(dāng)前時(shí)間,不管你在這里是否對(duì)其賦值。但是之后的save()是可以手動(dòng)賦值的。
也就是新實(shí)例化一個(gè)model,想手動(dòng)存其他時(shí)間,就需要對(duì)該實(shí)例save()之后賦值然后再save()
根評(píng)論
修改article_detail.html的js代碼,只需要發(fā)送3個(gè)參數(shù)即可。分別是content,atricle_id,parent_comment
{% extends "base.html" %}{% block content %}<div class="article_info"><h4 class="text-center">{{ article_obj.title }}</h4><div class="content">{{ article_obj.content|safe }}</div><div id="div_digg"><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"></div></div><div class="clearfix"></div><div class="comment"><p>評(píng)論列表</p><ul class="comment_list list-group">{% for comment in comment_list %}<li class="list-group-item"><div><a href="">#{{ forloop.counter }}樓</a> <span class="small">{{ comment.create_time|date:"Y-m-d H:i" }}</span> <a href="">{{ comment.user.username }}</a><a href="" class="pull-right"><span>回復(fù)</span></a></div><div><p>{{ comment.content }}</p></div></li>{% endfor %}</ul><p>發(fā)表評(píng)論</p><p>昵稱:<input type="text" id="tbCommentAuthor" class="author" disabled="disabled" size="50"value="{{ request.user.username }}"></p><div><textarea name="" id="comment_content" cols="60" rows="10"></textarea></div><input type="button" value="submit" class="btn btn-default comment_btn"></div></div>{% csrf_token %}<script>// 點(diǎn)贊和踩滅$(".action").click(function () {{#hasClass() 方法檢查被選元素是否包含指定的 class#}var is_up = $(this).hasClass("diggit");{#獲取提示的span標(biāo)簽#}var _this = $(this).children("span");{#判斷是否登錄#}if ("{{ request.user.username }}") {$.ajax({url: "/digg/",type: "post",data: {is_up: is_up,article_id: "{{ article_obj.pk }}",csrfmiddlewaretoken: $("[name='csrfmiddlewaretoken']").val()},success: function (data) {console.log(data);console.log(typeof data);if (data.state) {//提交成功var val = _this.text(); //獲取text值//在原有的基礎(chǔ)上加1。注意:一定要進(jìn)行類型轉(zhuǎn)換_this.text(parseInt(val) + 1)} else {// 重復(fù)提交var val = data.handled ? "您已經(jīng)推薦過(guò)!" : "您已經(jīng)反對(duì)過(guò)!";$("#digg_tips").html(val);setTimeout(function () {$("#digg_tips").html("") //清空提示文字}, 1000)}}})} else {location.href = "/login/";}})// 提交評(píng)論$(".comment_btn").click(function () {{#評(píng)論內(nèi)容#}var content = $("#comment_content").val();{#默認(rèn)為空#}var pid = "";$.ajax({url: "/comment/",type: "post",data: {content: content,article_id: "{{ article_obj.pk }}",pid: pid,csrfmiddlewaretoken: $("[name='csrfmiddlewaretoken']").val()},success: function (data) {console.log(data);// 清空輸入框的內(nèi)容$("#comment_content").val("")}})})</script>{% endblock %} View Code修改urls.py,增加路徑comment
urlpatterns = [path('admin/', admin.site.urls),path('login/', views.login),path('index/', views.index),path('logout/', views.logout),path('', views.index),#點(diǎn)贊或者踩滅path('digg/', views.digg),# 評(píng)論path('comment/', views.comment),#文章詳情re_path('(?P<username>\w+)/articles/(?P<article_id>\d+)/$', views.article_detail),# 跳轉(zhuǎn)re_path('(?P<username>\w+)/(?P<condition>category|tag|achrive)/(?P<params>.*)/$', views.homesite),# 個(gè)人站點(diǎn)re_path('(?P<username>\w+)/$', views.homesite), ] View Code修改views.py,增加視圖函數(shù)comment,完整代碼如下:
from django.shortcuts import render,HttpResponse,redirect from django.contrib import auth from blog.models import Article,UserInfo,Blog,Category,Tag,ArticleUpDown,Comment from django.db.models import Sum,Avg,Max,Min,Count from django.db.models import F import json from django.http import JsonResponse from django.db import transaction# Create your views here. def login(request):if request.method=="POST":user=request.POST.get("user")pwd=request.POST.get("pwd")# 用戶驗(yàn)證成功,返回user對(duì)象,否則返回Noneuser=auth.authenticate(username=user,password=pwd)if user:# 登錄,注冊(cè)session# 全局變量 request.user=當(dāng)前登陸對(duì)象(session中) auth.login(request,user)return redirect("/index/")return render(request,"login.html")def index(request):article_list=Article.objects.all()return render(request,"index.html",{"article_list":article_list})def logout(request): # 注銷 auth.logout(request)return redirect("/index/")def query_current_site(request,username): # 查詢當(dāng)前站點(diǎn)的博客標(biāo)題# 查詢當(dāng)前站點(diǎn)的用戶對(duì)象user = UserInfo.objects.filter(username=username).first()if not user:return render(request, "not_found.html")# 查詢當(dāng)前站點(diǎn)對(duì)象blog = user.blogreturn blogdef homesite(request,username,**kwargs): # 個(gè)人站點(diǎn)主頁(yè)print("kwargs", kwargs)blog = query_current_site(request,username)# 查詢當(dāng)前用戶發(fā)布的所有文章if not kwargs:article_list = Article.objects.filter(user__username=username)else:condition = kwargs.get("condition")params = kwargs.get("params")#判斷分類、隨筆、歸檔if condition == "category":article_list = Article.objects.filter(user__username=username).filter(category__title=params)elif condition == "tag":article_list = Article.objects.filter(user__username=username).filter(tags__title=params)else:year, month = params.split("/")article_list = Article.objects.filter(user__username=username).filter(create_time__year=year,create_time__month=month)return render(request,"homesite.html",{"blog":blog,"username":username,"article_list":article_list})def article_detail(request,username,article_id):blog = query_current_site(request,username)#查詢指定id的文章article_obj = Article.objects.filter(pk=article_id).first()user_id = UserInfo.objects.filter(username=username).first().nidreturn render(request,'article_detail.html',{"blog":blog,"username":username,'article_obj':article_obj,"user_id":user_id})def digg(request):print(request.POST)if request.method == "POST":#ajax發(fā)送的過(guò)來(lái)的true和false是字符串,使用json反序列化得到布爾值is_up = json.loads(request.POST.get("is_up"))article_id = request.POST.get("article_id")user_id = request.user.pkresponse = {"state": True, "msg": None} # 初始狀態(tài)#判斷當(dāng)前登錄用戶是否對(duì)這篇文章做過(guò)點(diǎn)贊或者踩滅操作obj = ArticleUpDown.objects.filter(user_id=user_id, article_id=article_id).first()if obj:response["state"] = False # 更改狀態(tài)response["handled"] = obj.is_up # 獲取之前的操作,返回true或者falseprint(obj.is_up)else:with transaction.atomic():#插入一條記錄new_obj = ArticleUpDown.objects.create(user_id=user_id, article_id=article_id, is_up=is_up)if is_up: # 判斷為推薦Article.objects.filter(pk=article_id).update(up_count=F("up_count")+1)else: # 反對(duì)Article.objects.filter(pk=article_id).update(down_count=F("down_count")+1)return JsonResponse(response)else:return HttpResponse("非法請(qǐng)求")def comment(request):print(request.POST)if request.method == "POST":# 獲取數(shù)據(jù)user_id = request.user.pkarticle_id = request.POST.get("article_id")content = request.POST.get("content")pid = request.POST.get("pid")# 生成評(píng)論對(duì)象with transaction.atomic(): # 增加事務(wù)# 評(píng)論表增加一條記錄comment = Comment.objects.create(user_id=user_id, article_id=article_id, content=content, parent_comment_id=pid)# 當(dāng)前文章的評(píng)論數(shù)加1Article.objects.filter(pk=article_id).update(comment_count=F("comment_count") + 1)response = {"state": False} # 初始狀態(tài)if comment.user_id: # 判斷返回值response = {"state": True}return JsonResponse(response) # 返回json對(duì)象else:return HttpResponse("非法請(qǐng)求") View Code?
刷新網(wǎng)頁(yè),發(fā)表一條評(píng)論
?查看表blog_comment,多了一條記錄
查看blog_article表,對(duì)應(yīng)的comment_count值,數(shù)值加1了。
?
?
渲染評(píng)論
修改article_detail視圖函數(shù),將評(píng)論列表傳給模板
def article_detail(request,username,article_id):blog = query_current_site(request,username)#查詢指定id的文章article_obj = Article.objects.filter(pk=article_id).first()user_id = UserInfo.objects.filter(username=username).first().nidcomment_list = Comment.objects.filter(article_id=article_id)dict = {"blog":blog,"username":username,'article_obj':article_obj,"user_id":user_id,"comment_list":comment_list,}return render(request,'article_detail.html',dict) View Code修改article_detail.html,使用for循環(huán),遍歷列表
{% extends "base.html" %}{% block content %}<div class="article_info"><h4 class="text-center">{{ article_obj.title }}</h4><div class="content">{{ article_obj.content|safe }}</div><div id="div_digg"><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"></div></div><div class="clearfix"></div><div class="comment"><p>評(píng)論列表</p><ul class="comment_list list-group">{% for comment in comment_list %}<li class="list-group-item"><div><a href="">#{{ forloop.counter }}樓</a> <span class="small">{{ comment.create_time|date:"Y-m-d H:i" }}</span> <a href="">{{ comment.user.username }}</a><a href="" class="pull-right"><span>回復(fù)</span></a></div><div><p>{{ comment.content }}</p></div></li>{% endfor %}</ul><p>發(fā)表評(píng)論</p><p>昵稱:<input type="text" id="tbCommentAuthor" class="author" disabled="disabled" size="50"value="{{ request.user.username }}"></p><div><textarea name="" id="comment_content" cols="60" rows="10"></textarea></div><input type="button" value="submit" class="btn btn-default comment_btn"></div></div>{% csrf_token %}<script>// 點(diǎn)贊和踩滅$(".action").click(function () {{#hasClass() 方法檢查被選元素是否包含指定的 class#}var is_up = $(this).hasClass("diggit");{#獲取提示的span標(biāo)簽#}var _this = $(this).children("span");{#判斷是否登錄#}if ("{{ request.user.username }}") {$.ajax({url: "/digg/",type: "post",data: {is_up: is_up,article_id: "{{ article_obj.pk }}",csrfmiddlewaretoken: $("[name='csrfmiddlewaretoken']").val()},success: function (data) {console.log(data);console.log(typeof data);if (data.state) {//提交成功var val = _this.text(); //獲取text值//在原有的基礎(chǔ)上加1。注意:一定要進(jìn)行類型轉(zhuǎn)換_this.text(parseInt(val) + 1)} else {// 重復(fù)提交var val = data.handled ? "您已經(jīng)推薦過(guò)!" : "您已經(jīng)反對(duì)過(guò)!";$("#digg_tips").html(val);setTimeout(function () {$("#digg_tips").html("") //清空提示文字}, 1000)}}})} else {location.href = "/login/";}})// 提交評(píng)論$(".comment_btn").click(function () {{#評(píng)論內(nèi)容#}var content = $("#comment_content").val();{#默認(rèn)為空#}var pid = "";$.ajax({url: "/comment/",type: "post",data: {content: content,article_id: "{{ article_obj.pk }}",pid: pid,csrfmiddlewaretoken: $("[name='csrfmiddlewaretoken']").val()},success: function (data) {console.log(data);// 清空輸入框的內(nèi)容$("#comment_content").val("")}})})</script>{% endblock %} View Code刷新網(wǎng)頁(yè),效果如下:
?
評(píng)論實(shí)時(shí)展示
上面提交評(píng)論后,網(wǎng)頁(yè)不能立即看到,需要刷新之后,才能看到。這樣體驗(yàn)不好!
查看博客園的評(píng)論,提交之后,會(huì)立即看到評(píng)論。此時(shí)不會(huì)顯示樓層,只會(huì)顯示評(píng)論內(nèi)容!
那么只需要提交成功之后,操作DOM,在評(píng)論列表中,追加一段li標(biāo)簽,展示一下,就可以了!
數(shù)據(jù)獲取問(wèn)題
那么內(nèi)容從何而來(lái)呢?
1.直接從html中獲取相關(guān)數(shù)據(jù)
2.讓服務(wù)器返回相關(guān)數(shù)據(jù),從響應(yīng)體中取數(shù)據(jù)。
針對(duì)這2種方式,我們選擇第2種!
為什么不選擇第一種呢?因?yàn)榈谝环N是原始輸入框中的值,那么存儲(chǔ)到數(shù)據(jù)后之后。就不一定還是輸入框的值!
服務(wù)器存儲(chǔ)到數(shù)據(jù)庫(kù)之前,會(huì)將提交的數(shù)據(jù),做一次處理!
我們想要的效果,就是不論是DOM操作,追加一段html代碼。還是刷新網(wǎng)頁(yè),加載評(píng)論。這2種方式,評(píng)論內(nèi)容是一摸一樣的!
所以,我們必須選擇第二種方案,讓服務(wù)器返回存儲(chǔ)的值給ajax,ajax操作DOM,最加一段html代碼,給用戶展示!
?
數(shù)據(jù)展示
修改comment視圖函數(shù),返回3個(gè)變量給ajax
def comment(request):print(request.POST)if request.method == "POST":# 獲取數(shù)據(jù)user_id = request.user.pkarticle_id = request.POST.get("article_id")content = request.POST.get("content")pid = request.POST.get("pid")# 生成評(píng)論對(duì)象with transaction.atomic(): # 增加事務(wù)# 評(píng)論表增加一條記錄comment = Comment.objects.create(user_id=user_id, article_id=article_id, content=content, parent_comment_id=pid)# 當(dāng)前文章的評(píng)論數(shù)加1Article.objects.filter(pk=article_id).update(comment_count=F("comment_count") + 1)response = {"state": False} # 初始狀態(tài)if comment.user_id: # 判斷返回值response = {"state": True}#響應(yīng)體增加3個(gè)變量response["timer"] = comment.create_time.strftime("%Y-%m-%d %X")response["content"] = comment.contentresponse["user"] = request.user.usernamereturn JsonResponse(response) # 返回json對(duì)象else:return HttpResponse("非法請(qǐng)求") View Code修改article_detail.html中的js代碼,使用append最加一段li標(biāo)簽
{% extends "base.html" %}{% block content %}<div class="article_info"><h4 class="text-center">{{ article_obj.title }}</h4><div class="content">{{ article_obj.content|safe }}</div><div id="div_digg"><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"></div></div><div class="clearfix"></div><div class="comment"><p>評(píng)論列表</p><ul class="comment_list list-group">{% for comment in comment_list %}<li class="list-group-item"><div><a href="">#{{ forloop.counter }}樓</a> <span class="small">{{ comment.create_time|date:"Y-m-d H:i" }}</span> <a href="">{{ comment.user.username }}</a><a href="" class="pull-right"><span>回復(fù)</span></a></div><div><p>{{ comment.content }}</p></div></li>{% endfor %}</ul><p>發(fā)表評(píng)論</p><p>昵稱:<input type="text" id="tbCommentAuthor" class="author" disabled="disabled" size="50"value="{{ request.user.username }}"></p><div><textarea name="" id="comment_content" cols="60" rows="10"></textarea></div><input type="button" value="submit" class="btn btn-default comment_btn"></div></div>{% csrf_token %}<script>// 點(diǎn)贊和踩滅$(".action").click(function () {{#hasClass() 方法檢查被選元素是否包含指定的 class#}var is_up = $(this).hasClass("diggit");{#獲取提示的span標(biāo)簽#}var _this = $(this).children("span");{#判斷是否登錄#}if ("{{ request.user.username }}") {$.ajax({url: "/digg/",type: "post",data: {is_up: is_up,article_id: "{{ article_obj.pk }}",csrfmiddlewaretoken: $("[name='csrfmiddlewaretoken']").val()},success: function (data) {console.log(data);console.log(typeof data);if (data.state) {//提交成功var val = _this.text(); //獲取text值//在原有的基礎(chǔ)上加1。注意:一定要進(jìn)行類型轉(zhuǎn)換_this.text(parseInt(val) + 1)} else {// 重復(fù)提交var val = data.handled ? "您已經(jīng)推薦過(guò)!" : "您已經(jīng)反對(duì)過(guò)!";$("#digg_tips").html(val);setTimeout(function () {$("#digg_tips").html("") //清空提示文字}, 1000)}}})} else {location.href = "/login/";}})// 提交評(píng)論$(".comment_btn").click(function () {{#評(píng)論內(nèi)容#}var content = $("#comment_content").val();{#默認(rèn)為空#}var pid = "";$.ajax({url: "/comment/",type: "post",data: {content: content,article_id: "{{ article_obj.pk }}",pid: pid,csrfmiddlewaretoken: $("[name='csrfmiddlewaretoken']").val()},success: function (data) {console.log(data);{#獲取3個(gè)值#}var comment_time = data.timer;var comment_content = data.content;var comment_user = data.user;{#組織li標(biāo)簽#}var $li = ` <li class="list-group-item"><div><span class="small">${comment_time}</span> <a href="">${comment_user}</a></div><div><p>${comment_content}</p></div></li>`;{#追加到評(píng)論列表中#}$(".comment_list").append($li);// 清空輸入框的內(nèi)容$("#comment_content").val("")}})})</script>{% endblock %} View Code刷新網(wǎng)頁(yè),重新評(píng)論,效果如下:
評(píng)論可以實(shí)時(shí)展示了
刷新網(wǎng)頁(yè),效果如下:
顯示出樓層
?
參考資料:
Django 基礎(chǔ)教程
轉(zhuǎn)載聲明:
作者:肖祥
出處:?https://www.cnblogs.com/xiao987334176/
轉(zhuǎn)載于:https://www.cnblogs.com/bqwzx/p/10198770.html
總結(jié)
以上是生活随笔為你收集整理的python 全栈开发,Day82(点赞和踩灭,用户评论)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 我是如何把一个15分钟的程序优化到了10
- 下一篇: python第三十二课——队列