Django,Ajax,Vue实现文章评论功能
Django評(píng)論
評(píng)論復(fù)雜的地方在于需要實(shí)現(xiàn)點(diǎn)擊提交評(píng)論后評(píng)論內(nèi)容需要立刻出現(xiàn)在下面,還要保持頁(yè)面位置不變,所以提交后不能整體刷新頁(yè)面,因?yàn)樗⑿乱院箜?yè)面肯定在最上面,而評(píng)論一般都在最下面,所以要用到Ajax
整個(gè)過(guò)程用到了Django,Vue.js,reqwest,REST_framework,ajax
展示評(píng)論內(nèi)容
展示評(píng)論內(nèi)容可以直接用Django從數(shù)據(jù)庫(kù)中取出數(shù)據(jù),然后在view中渲染到前端,但這里我想用Vue.js,為了減少django的工作量,提高效率吧
// pinglun.js let vue = new Vue({el : "#app",data : {pinglun : [{'評(píng)論者':'zhangsan','評(píng)論日期':'2019-6-5','評(píng)論時(shí)間':'17:47:23','評(píng)論內(nèi)容':'hahahha','對(duì)應(yīng)文章_id':'1'},{'評(píng)論者':'zhangsan1','評(píng)論日期':'2019-6-5','評(píng)論時(shí)間':'17:48:23','評(píng)論內(nèi)容':'hahffahha','對(duì)應(yīng)文章_id':'1'},],}, }) <!--pinglun.html--> <div id="app"><div class="alert alert-secondary" role="alert" id="pinglunlist"><div v-for="item in pinglun " ><h5>{{ item.評(píng)論者 }}</h5><p>{{ item.評(píng)論內(nèi)容 }}</p></div></div> </div>這樣js中data的數(shù)據(jù)就可以渲染到html頁(yè)面了,但我們需要從數(shù)據(jù)庫(kù)中拿到數(shù)據(jù),并且賦值給data中的pinglun,這里需要一個(gè)reqwest模塊,需要下載
npm i reqwest下載之后訪問(wèn)json文件里面的那個(gè)網(wǎng)址,下載壓縮包,解壓后里面有一個(gè)reqwest.js文件,要把這個(gè)文件引入,就和用Vue要引入Vue.main.js一樣,reqwest可以從一個(gè)url請(qǐng)求數(shù)據(jù),并且返回
// 這是官方api reqwest({// 要請(qǐng)求的路徑url: 'path/to/html'// 請(qǐng)求方式, method: 'post'// 請(qǐng)求時(shí)要攜帶的數(shù)據(jù), data: { foo: 'bar', baz: 100 }// 成功請(qǐng)求的回調(diào)函數(shù), success: function (resp) {// reap中就包含請(qǐng)求來(lái)的數(shù)據(jù)qwery('#content').html(resp)} }) reqwest({url: 'path/to/html', method: 'get', data: [ { name: 'foo', value: 'bar' }, { name: 'baz', value: 100 } ], success: function (resp) {qwery('#content').html(resp)} })應(yīng)為需要有一個(gè)請(qǐng)求的url,所以還需要做一個(gè)api接口,這里有兩種辦法,一種是用Django提供的HttpResponse和json直接將序列化后的json數(shù)據(jù)渲染到頁(yè)面,但這樣只能渲染成json類(lèi)型,并且存在文字編碼的問(wèn)題,還可以使用django-rest-framework框架,Django REST框架是一個(gè)功能強(qiáng)大且靈活的構(gòu)建Web api工具包
使用 HttpResponse ,不推薦
# urls.py path('api/json',views.injson),# views.py def injson(request):# 這里的info是手寫(xiě)的假數(shù)據(jù),若使用這種方法可以從數(shù)據(jù)庫(kù)中獲取相應(yīng)數(shù)據(jù)再用json.dumps序列化info = [{'評(píng)論者':'zhangsan','評(píng)論日期':'2019-6-5','評(píng)論時(shí)間':'17:47:23','評(píng)論內(nèi)容':'hahahha','對(duì)應(yīng)文章_id':'1'},{'評(píng)論者':'zhangsan1','評(píng)論日期':'2019-6-5','評(píng)論時(shí)間':'17:48:23','評(píng)論內(nèi)容':'hahffahha','對(duì)應(yīng)文章_id':'1'},]return HttpResponse(json.dumps(info))使用REST框架
先要安裝這個(gè)包以及依賴項(xiàng)
pip install djangorestframework pip install markdown # Markdown support for the browsable API. pip install django-filter # Filtering support其次需要在setting.py中配置app
INSTALLED_APPS = ['django.contrib.admin','django.contrib.auth','django.contrib.contenttypes','django.contrib.sessions','django.contrib.messages','django.contrib.staticfiles','myblog',# 這個(gè)就是REST依賴項(xiàng)'rest_framework' ]然后就要寫(xiě)api了,先把評(píng)論的model放出來(lái)
class 評(píng)論(models.Model):評(píng)論者=models.CharField(max_length=20)評(píng)論日期=models.DateField(auto_now_add=True)評(píng)論時(shí)間=models.TimeField(auto_now_add=True)評(píng)論內(nèi)容=models.TextField()對(duì)應(yīng)文章=models.ForeignKey('myblog.文章內(nèi)容',on_delete=models.CASCADE)然后編寫(xiě)api.py
# api.py# 引入model from .models import 評(píng)論 # REST提供的序列化工具 from rest_framework import serializers from rest_framework.response import Response from rest_framework.decorators import api_viewResonse
類(lèi)似于HttpResponse,用來(lái)渲染文本內(nèi)容,并根據(jù)內(nèi)容決定返回給用戶的數(shù)據(jù)類(lèi)型
Response(data, status=None, template_name=None, headers=None, content_type=None)# data:要渲染的數(shù)據(jù),可以是python的基本數(shù)據(jù)類(lèi)型 # status:狀態(tài)碼 # template_name:模板名稱 # headers:頭部信息 # content_type:內(nèi)容類(lèi)型的響應(yīng)因?yàn)镽esponse只能渲染python基本數(shù)據(jù)類(lèi)型,對(duì)于復(fù)雜的數(shù)據(jù)類(lèi)型,需要serializers.ModelSerializer來(lái)序列化
# api.pyclass PingLun(serializers.ModelSerializer):class Meta:depth = 1model = 評(píng)論fields = ('評(píng)論者','評(píng)論日期','評(píng)論內(nèi)容')然后就可以寫(xiě)url對(duì)應(yīng)的回調(diào)函數(shù)了,可以不使用api_view修飾器,但需要自己寫(xiě)一個(gè)判斷來(lái)判斷請(qǐng)求的類(lèi)型
@api_view(['GET']) def showdata(request):id = request.GET['id']print(id)datas=評(píng)論.objects.filter(對(duì)應(yīng)文章_id=id)PingLunData = PingLun(datas,many=True)return Response({'data':PingLunData.data})這時(shí)候訪問(wèn)api就可以看到優(yōu)雅的數(shù)據(jù)了,完了以后完善Vue,編寫(xiě)reqwest的內(nèi)容
let vue = new Vue({el : "#app",data : {// 開(kāi)始是一個(gè)空列表pinglun : [],},mounted(){console.log("賣(mài)個(gè)萌咋了!!!(>人<;)")this.getData()},computed : {},methods : {getData : function() {// 現(xiàn)在的this是window對(duì)象,等進(jìn)入reqwest,this就是rewqest對(duì)象了,所以提前保存thislet self = this// 只是為了獲取當(dāng)前文章的idlet myurl = window.location.hreflet id = myurl.toString().split("/").pop()reqwest({url: '/blog/api/showpinglun/?format=json', method: 'get', data: [{name: 'id',value: id}], success: function (data) {self._data.pinglun = data.data}})},} })到目前,就可以使用Vue從數(shù)據(jù)庫(kù)中獲取數(shù)據(jù)并渲染到前端了,總結(jié)一下:
提交評(píng)論
思路:
看著挺簡(jiǎn)單,但這里面有兩個(gè)問(wèn)題:
解決Ajax發(fā)送POST請(qǐng)求的CSRF問(wèn)題
這里有兩種思路
思路一:解決發(fā)現(xiàn)問(wèn)題的人
這種思路簡(jiǎn)單粗暴,既然問(wèn)題出在了csrf驗(yàn)證上,那就不讓他進(jìn)行驗(yàn)證就好了嘛,組織進(jìn)行驗(yàn)證有兩個(gè)簡(jiǎn)單的辦法
使用裝飾器
在要取消進(jìn)行csrf驗(yàn)證的視圖函數(shù)上添加修飾器@csrf_exempt
from django.views.decorators.csrf import csrf_exempt @csrf_exempt def demo(request):pass趕盡殺絕法
第二種是狼人的做法,比較絕,直接從setting中刪除csrf驗(yàn)證的依賴項(xiàng)
MIDDLEWARE = ['django.middleware.security.SecurityMiddleware','django.contrib.sessions.middleware.SessionMiddleware','django.middleware.common.CommonMiddleware',# 就是這個(gè),刪了就ok,但安全性嘛,就。。。。'django.middleware.csrf.CsrfViewMiddleware','django.contrib.auth.middleware.AuthenticationMiddleware','django.contrib.messages.middleware.MessageMiddleware','django.middleware.clickjacking.XFrameOptionsMiddleware', ]思路二:釜底抽薪
思路一實(shí)現(xiàn)簡(jiǎn)單,一勞永逸,看似不錯(cuò),但取消csrf驗(yàn)證會(huì)讓網(wǎng)站處于很危險(xiǎn)的境地,建議不要這樣用,第二種方法就要優(yōu)雅很多,首先要知道Django是怎樣防御CSRF攻擊的,CSRF,跨站請(qǐng)求偽造攻擊,是攻擊者利用用戶登錄保存的cookies偽裝成用戶進(jìn)行非發(fā)操作的攻擊方式,比如攻擊者在某網(wǎng)站留下了一個(gè)付款的鏈接www.xxxx.com/shop?money=500;to=hark(注意,這個(gè)鏈接已經(jīng)設(shè)計(jì)了用戶驗(yàn)證,只有正確登錄后才能付款,沒(méi)登錄直接訪問(wèn)這個(gè)鏈接會(huì)被重定向到登錄界面),一個(gè)受害者在訪問(wèn)這個(gè)釣魚(yú)鏈接之前正好訪問(wèn)過(guò)付款的那個(gè)網(wǎng)站,并登錄留下了自己的cookies,這時(shí)候她再去訪問(wèn)那個(gè)釣魚(yú)鏈接,瀏覽器就會(huì)檢查本地有沒(méi)有對(duì)應(yīng)的cookies文件,正好有,系統(tǒng)就認(rèn)為是他本人在付款,這就是一次csrf攻擊,csrf的特點(diǎn)是攻擊者并沒(méi)有拿到受害人的cookies,針對(duì)這個(gè)特點(diǎn),django的處理辦法是在cookies中增加一個(gè)csrf_token字段,內(nèi)容為隨機(jī)序列,同時(shí)表單提交時(shí)也把這個(gè)序列作為表單的一項(xiàng)同表單數(shù)據(jù)一起提交給后端做驗(yàn)證,如果表單中的序列與cookies中的序列不一樣,就定義為csrf攻擊,在Debug模式下會(huì)拋出403錯(cuò)誤。
根據(jù)這個(gè),我們只要在Ajax的請(qǐng)求頭中加上cookies中的那個(gè)字段就可以了嘛,其實(shí)如果不懂csrf,直接在瀏覽器開(kāi)發(fā)者工具里對(duì)比我們的Ajax請(qǐng)求頭和正常的POST請(qǐng)求頭就會(huì)發(fā)現(xiàn)我們少了X-CSRFToken這個(gè)字段,獲取本站cookies中的csrftoken字段,添加到請(qǐng)求頭中就可以。其實(shí)對(duì)比發(fā)現(xiàn)我們還缺了一項(xiàng),不寫(xiě)會(huì)報(bào)500錯(cuò)誤,Content-Type
setRequestHeader必須寫(xiě)在open之后
// js獲取cookies依賴下面的庫(kù) <script src="https://cdn.jsdelivr.net/npm/js-cookie@2/src/js.cookie.min.js"></script>// 獲取cookies let csrftoken = Cookies.get('csrftoken');// 設(shè)置請(qǐng)求頭 XHRObject.setRequestHeader("X-CSRFToken", csrftoken);Ajax發(fā)送POST請(qǐng)求
<div id="app"><div class="alert alert-primary" role="alert"><p>評(píng)論<<</p><hr /><div class="form-group"><label for="exampleFormControlInput1">評(píng)論者:</label><input type="text" class="form-control" id="exampleFormControlInput1" placeholder="請(qǐng)輸入你的姓名" name="評(píng)論者" maxlength="20" required=""></div><div class="form-group"><label for="exampleFormControlTextarea1">有問(wèn)題?不妨寫(xiě)下了...</label><textarea class="form-control" name="評(píng)論內(nèi)容" id="exampleFormControlTextarea1" rows="3"></textarea></div><hr><button type="submit" name="評(píng)論提交" οnclick="XMLDoc()"">提交評(píng)論</button></div> function XMLDoc(){let XHRObject// 適配瀏覽器if(window.XMLHttpRequest){XHRObject = new XMLHttpRequest}else{XHRObject =new ActiveXObject("Microsoft.XMLHTTP")}// 接收XHRObject.onreadystatechange = function () {if (XHRObject.status == 200 & XHRObject.readyState == 4) {}}// 獲取文章idlet url = window.location.hreflet id = url.toString().split("/").pop()// 獲取csrftokenlet csrftoken = Cookies.get('csrftoken');// 獲取表單數(shù)據(jù)let name = document.getElementById('exampleFormControlInput1').valuelet neirong = document.getElementById('exampleFormControlTextarea1').value// 發(fā)送POST請(qǐng)求XHRObject.open("POST","/blog/api/postpinglun/?format=json",true)// 設(shè)置請(qǐng)求頭XHRObject.setRequestHeader("X-CSRFToken", csrftoken);XHRObject.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");// 發(fā)送請(qǐng)求,只接受一個(gè)字符串,鍵值用=連接,多個(gè)鍵值對(duì)用&連接XHRObject.send('name='+name+'&neirong='+neirong+'&id='+id.toString())document.getElementById('exampleFormControlInput1').value = ""document.getElementById('exampleFormControlTextarea1').value = ""}api保存數(shù)據(jù)到數(shù)據(jù)庫(kù)
@api_view(['POST']) def postdata(request):# 獲取Ajax傳來(lái)的表單信息name = request.POST['name']neirong = request.POST['neirong']id = request.POST['id']# 保存到數(shù)據(jù)庫(kù)obj=評(píng)論(評(píng)論者 = name,評(píng)論日期 = datetime.datetime.now().strftime('%Y-%m-%d'),評(píng)論時(shí)間 = datetime.datetime.now().strftime('%H:%M:%S'),評(píng)論內(nèi)容 = neirong,對(duì)應(yīng)文章_id = id)obj.save()提交數(shù)據(jù)時(shí)更新下方評(píng)論列表
要在提交時(shí)更新,就要綁定兩個(gè)單擊事件,一個(gè)是Ajax的,用來(lái)保存數(shù)據(jù),另一個(gè)是Vue的,用來(lái)更新數(shù)據(jù),這里可以直接調(diào)用之前的getData函數(shù)
<button type="submit" name="評(píng)論提交" onclick="XMLDoc()" @click="getData()">提交評(píng)論</button>總結(jié)
以上是生活随笔為你收集整理的Django,Ajax,Vue实现文章评论功能的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 该如何实现EDIUS中的多机位编辑
- 下一篇: html5倒计时秒杀怎么做,vue 设