一、點贊功能
思路是這樣的:
1、對點贊和踩都設置一個相同的class,然后對這個class綁定點擊事件
2、點擊觸發ajax請求,我們對贊的標簽設置了一個class屬性,對踩的標簽沒有設置這個class屬性,如果我們點擊的標簽有這個class屬性,則我們認為這次點擊的贊,如果沒有,則我們認為是踩
3、ajax向后台發的數據有文章的id、和這次是踩還是贊的信息即可,因為這次操作的用戶,可以直接從后台獲取,因為我們用了django默認的auth模塊,這里要注意,由於ajax這次發的是post請求,則需要把crsf帶上
4、后端拿到信息后,先去點贊表中查詢,如果沒有點贊或者踩過,則直接在數據庫中創建信息即可,如果有的話,因為我們這里的設計是這樣的,如果點過贊或者踩過,則不允許再次點贊和踩,根據數據庫中不同的狀態通過dict的方式用Httpresponse的方式發送給前端
5、前端的ajax的success函數接受到后,我們前端可以有兩張方式處理
5_1、方式1,可以直接更新當前頁面就可以了,但是這樣就會加重服務器的負擔
5_2、方式2,可以利用ajax的局部刷新的原理,如果操作成功的話,獲取到當前贊或者踩標簽的數值,然后對該數值+1,就可以了,如果失敗,則把報錯信息打印在前端即可,這里我們還可以用一個setTimeout的函數,可以設置過多少秒執行某個函數,我們這里設置1000ms后,把報錯信息清空即可
6、這里還有一個小技巧要分享一下,我們點擊贊或者踩,如何獲取當前的文章的id呢,我們當然可以通過js的方式去尋找這個id,但是有一個非常簡單的方法,我們可以對贊或者踩這個標簽設置一個自定義屬性,這個屬性就這個文章的id,這樣,我們就可以通過$("this").attr("屬性名稱")來獲取我們的文章的id了
先看下前端的代碼,如下這段代碼是我直接從博客園上爬下來的
<div class="poll clearfix"> <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" style="color: red"></div> </div> </div>
這里需要注意,同時也要把css文件也扒下來,然后放在的我們的css的block塊中
{% block css %} <link rel="stylesheet" href="/static/css/article.css"> {% endblock %}
然后我們看下js的代碼,我們這里是用ajax的方式實現點贊和踩的功能,這里我們把點贊和踩的js代碼單獨放在了一個js文件中,然后在script標簽中引入
<script src="/static/js/for_artic_desc.js"></script>
下面我們重點看下js的代碼
$("#div_digg .action").click(function () { if ($(".info").attr("username")){ var is_up = $(this).hasClass("diggit"); // {# 如果有diggit這個class,則為true,如果沒有diggit,則為false,如果為true,則為贊,如果為false則為踩#} // // {# var article_id = {{ article_obj.nid }};#} // {##} // {# var article_title = {{ article_obj.title }};#} // {# 如果在js中使用模板渲染的話,如果直接按照上面的方式渲染#} // {# #} // {# 則如果渲染出來的值是字符串,則js會把他當做變量,會報錯#} // {# 則如果渲染出來的值是數字,則js會把他當做一個數字,不會報錯#} // {# #} // {# #} // {# 為了解決這個問題,如果我們在js要使用模板語言,則對大括號外面加一個引號就可以了#} // var article_id = "{{ article_obj.nid }}"; var article_id = $(".info").attr("artic_id"); var article_title = "{{ article_obj.title }}"; $.ajax( { url: "/app1/up_down/", type: "post", data: { "article_id": article_id, "article_title": article_title, "is_up": is_up, "csrfmiddlewaretoken": $("[name='csrfmiddlewaretoken']").val(), }, success: function (data) { data = JSON.parse(data); if (data.state) { 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 { $("#digg-tips").text(data.error_code); setTimeout(function () { $("#digg_tips").text("aaaaaaaa"); console.log("123") },1000) } } } ) }else { location.href = "/app1/login/" } })
上面的代碼比較簡單,就是獲取需要的值,然后通過ajax通過post方式發送給后端
首先判斷有沒有登陸
如果沒有登陸則跳轉到登陸頁面
如果有登陸,則首先拿到is_up這個變量的值,這里我們通過判斷我們點擊的標簽是否有某個class屬性來判斷,如果有則is_up為true,如果沒有,則is_up為false
這里還需要注意,在js代碼也可以拿到大括號里的值,但是必須要用引號括起來,不然js會把大括號中的值當做一個變量來處理
這里還有一個小技巧,我們可以寫一個標簽,然后這個為這個標簽加上自定義的屬性,而這些屬性的值就是我們要發給后端的值,我們就可以直接找到這個標簽,然后通過attr方法獲取到相應的屬性,然后就可以發送給后端就可以了
比如我們這里就用這個標簽通過自定義的方式存儲我們要往后端發送的數據
<div class="info" artic_id="{{ article_obj.nid }}" username="{{ request.user.username }}"></div>
這里還要注意,ajax發送數據,需要在data中添加下面的代碼,才可以解決crsf問題
"csrfmiddlewaretoken": $("[name='csrfmiddlewaretoken']").val(),
success中的函數就很簡單了
success: function (data) { data = JSON.parse(data); if (data.state) { 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 { $("#digg-tips").text(data.error_code); setTimeout(function () { $("#digg_tips").text("aaaaaaaa"); console.log("123") },1000) } }
最后我們看下后端的代碼
from django.views.decorators.csrf import csrf_exempt from django.db.models import F # @csrf_exempt import json def up_down(request): ret = {"state":True,"error_code":""} print(request.POST.get("article_id")) print(request.POST.get("article_title")) is_up = request.POST.get("is_up") user_obj = models.Userinfo.objects.get(username=request.user) try: models.ArticleUpDown.objects.create( user = user_obj, article = models.Article.objects.get(nid=request.POST.get("article_id")), is_up = json.loads(is_up) ) except Exception as e: flag = models.ArticleUpDown.objects.filter(user=user_obj,article=models.Article.objects.get(nid=request.POST.get("article_id")))[0].is_up if flag: if json.loads(is_up): ret = {"state": False, "error_code": "你已經頂過該博客,不能在頂了"} else: ret = {"state": False, "error_code": "你已經頂過該博客,不能在踩了"} else: if json.loads(is_up): ret = {"state": False, "error_code": "你已經踩過該博客,不能在頂了"} else: ret = {"state": False, "error_code": "你已經踩過該博客,不能在踩了"} else: if json.loads(is_up): models.Article.objects.filter(nid=request.POST.get("article_id")).update( up_count = F("up_count") + 1 ) else: models.Article.objects.filter(nid=request.POST.get("article_id")).update( down_count = F("down_count") + 1 ) return HttpResponse(json.dumps(ret))
后端的python代碼就非常熟悉了,這里就不做講解了
二、評論功能
評論功能的思路是這樣的
我們先捋一下我們要實現的需求
需求1、實現根評論
需求2、用評論樓的方式實現子評論
需求3、用評論樹的方式實現子評論
實現根評論
1、利用ajax,把文章的id和評論的內容發送給后端的視圖函數,后端函數處理成功,返回給前端
2、前端直接刷新當前頁面,顯示評論即可
實現評論樓的方式子評論
1、對回復的按鈕綁定一個focus事件,點擊這個事件會做兩件事情,a、把當前的標簽定位的評論內容輸入框,b、獲取這次評論的評論者,然后做字符串拼接,把@ + “用戶名”的值輸入到評論框中
2、然后把評論的內容,文章的id,被評論的id發送給后端
3、后端處理成功后,返回前端,前端刷新頁面就可以了
實現評論樹的方式子評論
1、也是用ajax的方式實現,首先直接在script標簽中定義一個函數,這個函數只要頁面一加載就會執行,后端把這個文章的所有的評論按照評論的時間排序,最后都返回給前端
2、前端接受到后端所有的評論之后,循環所有的數據,首先把所有的跟評論顯示出來,這里我們定義一個標簽的字符串,然后把所有的根評論插入到我們的評論樹的父標簽中,當前我們需要為插入的標簽賦值一個自定義的屬性,這個屬性的值就是這個標簽的id值
3、然后在循環子評論的標簽,首先找到這個標簽評論的父評論的的標簽,通過$("[屬性=父評論的id]"),然后把標簽的字符串格式通過append方式插入進去就可以了
下面我們看下具體的代碼
前端的html的代碼如下,當然也是從博客園上扒下來的
{% if request.user.username %} <div class="comments"> <p> 昵稱:<input type="text" id="tbCommentAuthor" class="author" disabled="disabled" size="50" value="{{ request.user.username }}"> <p>評論內容:</p> <textarea name="comments" id="comments_id" cols="57" rows="10"></textarea> <p><input type="button" id="ajax_comment" value="提交評論"></p> </p> </div> {% else %} <p> <a href="/app1/login/">如果要評論,請先登陸</a> </p> {% endif %}
這里我們先做了一個判斷,如果用戶登陸,即request.user.username有值,則認為登陸成功,則允許評論,如果沒有值,則返回到登陸頁面
這里我們用ajax進行評論的提交
下面我們看下js代碼是如何實現的
has_p = false $("#ajax_comment").bind("click",function () { var content = $("#comments_id").val(); var article = $(".info").attr("artic_id"); {# alert($(this).attr("username"))#} $.ajax( { url:"/app1/add_comment/", type:"post", data:{ "content":content, "article":article, "csrfmiddlewaretoken": $("[name='csrfmiddlewaretoken']").val(), "has_p":has_p }, success:function (data) { setTimeout(function () { $("#comments_id").val("") },2000); {# 重新加載本頁面#} document.location.reload(); has_p = "" } } ) })
這里,我們首先定義了一個全局的變量has_p。如果這個變量有值,則說明是一個子評論,如果這個變量沒有值,則說明這是一個根評論,這里我們先看下根評論
通過ajax把評論的內容和文章的id和has_p的值發送給后台,這里也要注意,因為我們是post,需要注意crsf的處理
我們這里也是找了一個沒用的標簽存儲我們需要的文章的id,方便我們在js去查找,這里標簽的作用就是方便我們查找文章的id
<div class="info" artic_id="{{ article_obj.nid }}" username="{{ request.user.username }}"></div>
然后我們看下后端的代碼
def add_comment(request): ret = {"state":True,"errorcode":""} u = models.Userinfo.objects.get(username=request.user.username) content = request.POST.get("content") has_p = request.POST.get("has_p") article = models.Article.objects.get(nid=request.POST.get("article")) if not json.loads(request.POST.get("has_p")): models.Comment.objects.create( article = article, user = u, content = content, ) return HttpResponse(json.dumps(ret)) # comment_list = models.Comment.objects.all() # return re else: import re obj = re.split("\n",content,1) models.Comment.objects.create( article = article, user = u, content = obj[1], parent_comment_id = int(has_p) ) return HttpResponse(json.dumps(ret))
在這里,我們先把post的請求中的數據拿到,然后判斷一下has_p是否有值,如果沒有值,則是根評論,如果有值,則說明是子評論
拿到插入數據需要的值,調用create方法在數據庫中創建數據即可,然后返回一個json.dumps("dict")的json字符串
前端拿到后端發過來的數據,這里我們做異常處理,只做基本功能的實現,把當前的評論框的內容清空,然后重新加載頁面
success:function (data) { setTimeout(function () { $("#comments_id").val("") },2000); {# 重新加載本頁面#} document.location.reload(); has_p = "" }
然后看下評論樓的方式實現子評論
先看下html的代碼
<h3>評論樓</h3> <div class="comment_list"> {% for comment in comment_list %} <div class="feedbackItem"> <div class="feedbackListTitle"><a href="#4031379" class="layer">#{{ forloop.counter }}樓</a><a name="4031379" id="comment_anchor_4031379"></a></div> <div class="feedbackListSubtitle"> <span class="comment_date">{{ comment.create_time }}</span> | <a id="a_comment_author_4031379" href="#" target="_blank">{{ comment.user }}</a><br> <div align="left"> {% if comment.parent_comment %} <p><a href="">@{{ comment.parent_comment.user.username }} {{ comment.parent_comment.content }}</a></p> {% endif %} <div id="{{ comment.nid }}" class="blog_comment_body">{{ comment.content }}</div> <div class="comment_vote"><a class="comment_digg" style="cursor: pointer" username={{ comment.user }} cid="{{ comment.nid }}">回復</a></div> </div> </div> </div> {% endfor %} </div>
html代碼是從博客園拔下來了的,這里我們對回復這個a標簽綁定了一個事件,設置了一個屬性,這個屬性就是cid屬性,屬性的值就是這次評論的id,因為我們這里做的子評論,所以當前回復的所代碼的回復就是我們下次評論的父評論,這里也是方便我們拿到父評論的id,直接通過$("this").attr("cid")就可以拿到值
另外我們對這個a標簽綁定了一個focus的事件,會把當前的鼠標定位到評論框中,然后通過字符串拼接,拿到一個@+用戶名+“\n”的字符串,然后賦值給評論框中
另外我們在這個事件函數中還對has_p這個變量進行賦值了,賦值為這次評論的id
$(".feedbackItem .comment_digg").bind("click",function () { $("#comments_id").focus() ; var uname = $(this).attr("username"); var temp = "@" + uname + "\n"; has_p = $(this).attr("cid") $("#comments_id").val(temp); })
點擊提交評論,就和父評論是一樣的,唯一的不一樣的就是has_p這次有值了,我們直接看視圖函數是如何處理的
import re obj = re.split("\n",content,1) models.Comment.objects.create( article = article, user = u, content = obj[1], parent_comment_id = int(has_p) ) return HttpResponse(json.dumps(ret))
后端的處理很簡單,首先要對前端發過來的字符串做split,把接受到的評論的按照第一個換行符做分割,取列表的第二個值,這里就是我們的評論的真正的內容,然后更新數據庫就可以了
在前端渲染評論的時候,這里我們先判斷該評論是否有父評論,對兩種情況做分別的處理
{% if comment.parent_comment %} <p><a href="">@{{ comment.parent_comment.user.username }} {{ comment.parent_comment.content }}</a></p> {% endif %} <div id="{{ comment.nid }}" class="blog_comment_body">{{ comment.content }}</div> <div class="comment_vote"><a class="comment_digg" style="cursor: pointer" username={{ comment.user }} cid="{{ comment.nid }}">回復</a></div> </div>
最后我們看下評論樹的實現方式
首先我們在scrip中直接寫了一個ajax,實現的效果就是頁面加載成功后,他會把我們的文章id發送給后端,不需要綁定什么事件
先看下這段ajax的代碼,先不要看success中的函數
$.ajax( { url:"/app1/comment_tree/" + "{{ article_obj.nid }}", success:function (data) { $.each(data,function (i,comment_obj) { var s = '<div class="comment_item" comment_id=' + comment_obj[0] + '> <span class="content">' + comment_obj[3] + '</span> </div>' if (comment_obj[2] == null){ $(".comment_tree").append(s) }else { console.log("[comment_id=" + comment_obj[2] + "]") $("[comment_id=" + comment_obj[2] + "]").append(s) } {# console.log(comment_obj)#} }) } } )
然后我們去看下視圖函數的處理
from django.http import JsonResponse def comment_tree(request,nid): ret = models.Comment.objects.filter(article__nid=int(nid)).order_by("create_time").values_list("nid","create_time","parent_comment","content","user") comment_tree_list = [] print("=" * 120) for i in ret: comment_tree_list.append(i) print(comment_tree_list,type(comment_tree_list)) return JsonResponse(list(ret),safe=False)
在后端的視圖函數中,我們拿到這個文章的所有的評論,然后發送給前端
這次我們看下success中的函數,
通過$.each來循環后端發來的所有的數據,然后把每個評論都做字符串拼接,弄成一個html標簽的字符串
首先判斷是否是根評論,如果是根評論,則直接把字符串插入到評論樹中即可
如果不是根評論,則一定是子評論,則通過has_p的值,查找到父評論的標簽,然后把這個字符串插入到父評論中,這里怎么找到的父評論的標簽,這里有一個點睛之筆,我們看下我們的htlm標簽的字符串
var s = '<div class="comment_item" comment_id=' + comment_obj[0] + '> <span class="content">' + comment_obj[3] + '</span> </div>'
這個標簽有一個comment_id的自定義屬性,我們直接通過自定義屬性查找父評論的標簽即可
通過屬性查找器,直接找父評論的標簽,然后插入這次的評論的字符串即可