日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 前端技术 > vue >内容正文

vue

Django,Ajax,Vue实现文章评论功能

發(fā)布時(shí)間:2023/12/20 vue 27 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Django,Ajax,Vue实现文章评论功能 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

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_view

Resonse

類(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é)一下:

  • 要用Vue渲染數(shù)據(jù),數(shù)據(jù)就必須在data中,但我們又不能寫(xiě)死,必須從一個(gè)地方動(dòng)態(tài)獲取數(shù)據(jù)
  • 這個(gè)地方就是api,django有一個(gè)模塊REST專門(mén)用來(lái)建立api
  • 要?jiǎng)討B(tài)請(qǐng)求數(shù)據(jù),需要用到一個(gè)框架 reqwest
  • REST渲染數(shù)據(jù)用到了Resonse,但它只能渲染python基本數(shù)據(jù),從Object.filter()得到的顯然不是,因此還要序列化數(shù)據(jù),這里用到了serializers
  • 另外,還需要api_view這個(gè)裝飾器判斷請(qǐng)求類(lèi)型
  • 提交評(píng)論

    思路:

  • 使用POST請(qǐng)求
  • 把表單內(nèi)容交給api,api再保存到數(shù)據(jù)庫(kù)
  • 看著挺簡(jiǎn)單,但這里面有兩個(gè)問(wèn)題:

  • Django要求所有POST請(qǐng)求進(jìn)行CSRF驗(yàn)證,使用正常的表單我們可以添加{{csrf_token}},Django會(huì)自動(dòng)在Cookies中添加csrf驗(yàn)證用的隨機(jī)序列,用reqwest怎么辦
  • 一般情況下提交評(píng)論后評(píng)論會(huì)立刻顯示在下面,怎么做
  • 解決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)題。

    如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。