1. 程式人生 > >django框架之BBS項目之評論功能

django框架之BBS項目之評論功能

class pre 存在 nec 模板 ftime () isp package

內容回顧
1. BBS項目 CMS
1. 登錄
1. form組件
2. auth模塊
3. 驗證碼
2. 註冊
1. form組件
1. 生成html代碼
直接for循環form_obj,就能夠遍歷所有字段
2. 驗證
1. 默認的那些驗證
2. 正則的驗證
3. 全局鉤子做確認密碼的驗證
4. 判斷用戶名是否已經存在
1. input框失去焦點就發ajax到後端判斷
2. form組件中使用局部鉤子來判斷

2. auth模塊 --> 擴展auth_user表 --> create_user()
1. UserInfo這個類裏面,avatar是一個FileField
avatar = models.FileField(upload_to="avatars/", default="avatars/default.png")
2. 註意事項:
1. FileField保存的是一個路徑,而不是一個文件
2. upload_to:具體保存的文件路徑就會在media目錄下
3. 上傳頭像
1. ajax如何上傳文件
2. Django中media的配置
1. settings.py中:
- MEDIA_URL:別名
- MEDIA_ROOT:給用戶上傳的所有文件指定一個存放目錄
2. urls.py中:
from django.views.static import serve
from django.conf import settings

url(r‘^media/(?P<path>.*)‘, serve, {"document_root": settings.MEDIA_ROOT})
3. 博客首頁
1. 文章列表(排樣式)
2. 分頁
1. 封裝的要徹底
2. 封裝後的結果要有普適性(url要寫成配置項)
4. 個人博客主頁
1. 分類
1. 文章分類
2. 文章標簽
2. 日期歸檔
1. 日期格式化函數 --> MySQL內置的函數都有哪一些? --> 《漫畫數據庫》
1. MySQL:DATE_FORMAT(‘字段‘, ‘格式‘)
2. sqlite:strftime(‘格式‘, ‘字段‘)

2. ORM中如何執行原生的SQL語句
1. 使用extra()在執行ORM查詢的同時,嵌入一些SQL語句
2. 直接執行原生SQL語句
from django.db import connection
cursor = connection.cursor()
cursor.execute(‘select id from userinfo;‘)
cursor.fetchall()
3. 分組聚合
QuertySet.annotate() --> 分組,前面查的是什麽字段就按什麽字段分組
QuertySet.aggregate() --> 聚合,給QuerySet中的每個對象多一個屬性

4. 4合1路由
不同的路由可以使用同一個視圖函數!!! --> 視圖函數中通過參數的不同,實現不同的功能
5. 文章詳情頁
1. 母板繼承
2. inclusion_tag
1. 返回一段HTML代碼,用數據填充的HTML代碼
2. 具體的寫法
1. 在app下面創建一個名為 templatetags 的 Python Package
2. 在上面的包中創建一個 py文件
3. 按照inclusion_tag的格式要求寫功能函數

from django import template
register = template.Library()

@register.inclusion_tag(file=‘xx.html‘)
def show_menu(*arg):
...
return {"k1": "v1"}
(xx.html中使用k1這個變量)
3. 點贊
1. ajax發送點贊的請求
1. 點贊必須是登錄用戶,沒登錄跳轉到登錄頁面
2. 不能給自己的文章點贊
3. 一個用戶只能給一篇文章點一次贊或踩一次

2. 後端創建點贊記錄(事務操作)
1. 創建新的點贊記錄
2. 去對應的文章表裏把點贊數更新一個
3. ORM事務操作
from django.db import transaction

with transaction.atomic():
sql1;
sql2;

4. Django模板語言裏面的JS代碼
如何在js中引用模板語言的變量,註意加引號!!!

接下來就開始寫評論區的功能了,首先就是在頁面布局:

我們希望吧頁面布局成這個樣式:

技術分享圖片

在bootstrap找到樣式之後,作相應的改動:我們先想一下,在這個區域需要哪些數據,首先就是評論發表的時間,評論的作者,還有評論的內容。所以在文章詳情頁面的試圖函數中應該傳一些comment的參數。之後再頁面中,通過for循環,逐個展示評論:

<!--評論展示區 開始-->
    <h4>評論</h4>
    <div class="list-group comment-list">
        {% for comment in comment_list %}
            
<div href="#" class="list-group-item"> <h4 class="list-group-item-heading comment-header"> <span>#{{ forloop.counter }}樓</span> <span>{{ comment.create_time|date:Y-m-d H:i }}</span> <span>{{ comment.user.username }}</span> <span></span> </h4> <p class
="list-group-item-text">{{ comment.content }}</p> </div> {% endfor %} </div> <!--評論展示區 結束-->

接下來就是發表評論的區域了:

技術分享圖片同樣是先布局,然後html代碼:

<!--發表評論區 開始-->
    <h4>發表評論</h4>
    <div>昵稱:
        <input type="text"value="{{ request.user.username }}" disabled>
    </div>
    <div>
        <p>評論內容:</p>
        <textarea cols="60" rows="10"></textarea>
    </div>
    <div>
        <button class="btn btn-success">提交</button>
    </div>
    <!--發表評論區 結束-->

接下來就是發送ajax請求了。

我們能夠很容易的寫出來這步:

 //給評論按鈕綁定點擊事件
    $(#submit-comment).click(function () {
        var userid = {{ request.user.id }};
        var articleid = {{ article_obj.id }};
        var content = $(#new-comment).val();
        var csrf_token = $([name="csrfmiddlewaretoken"]).val();
        $.ajax({
            url:/comment/,
            type:post,
            data:{
                user_id:userid,
                article_id:articleid,
                content:content,
                csrfmiddlewaretoken:csrf_token,
            },
            success:function (res) {
                console.log(res)
            }
        })
    })
試圖函數也可以這樣寫;

def
comment(request): if request.method == POST: res = {code:0} user_id = request.POST.get(user_id) article_id = request.POST.get(article_id) content = request.POST.get(content)
    
with transaction.atomic():
     # 1.先去創建新評論
comment_obj = models.Comment.objects.create(user_id=user_id, article_id=article_id, content=content)
     # 2.再去更新該文章的評論數
models.Article.objects.update(comment_count=F(‘comment_count‘)+1)
   return JsonResponse(res)

這樣基本的評論功能已經寫出來了,給文章評論之後,刷新之後在頁面上就能夠展示出評論內容,

但是我們實際的評論不是這樣的,當我們評論之後,評論內容就會馬上出現在展示區,不用通過刷新。這一步怎麽實現呢?

這就要求我們在發送請求成功之後的success函數中將它通過js操作展示出來,說白了就是在原來評論展示區的下面再添加一個展示區。所以試圖函數要返回一些數據:

views.py代碼:

def comment(request):
    if request.method == POST:
        res = {code:0}
        user_id = request.POST.get(user_id)
        article_id = request.POST.get(article_id)
        content = request.POST.get(content)     
    
with transaction.atomic():
     # 1.先去創建新評論
comment_obj = models.Comment.objects.create(user_id=user_id, article_id=article_id, content=content)
     # 2.再去更新該文章的評論數
models.Article.objects.update(comment_count=F(‘comment_count‘)+1)
    res[data] = { 
      ‘id:comment_obj.id,
      ‘username:comment_obj.user.username,
      ‘content:comment_obj.content,
      ‘create_time:comment_obj.create_time.strftime("Y-m-d") # 這部如果看不懂的話就去復習一下那三種時間格式的相互轉化 }

    return JsonResponse(res)

在說js操作代碼之前,我們先來復習一個知識:就是js中的字符串格式化輸出。

技術分享圖片

 # 給評論按鈕綁定點擊事件
$(#submit-comment).click(function () { var userid = {{ request.user.id }}; var articleid = {{ article_obj.id }}; var content = $(#new-comment).val(); var csrf_token = $([name="csrfmiddlewaretoken"]).val(); $.ajax({ url:/comment/, type:post, data:{ user_id:userid, article_id:articleid, content:content, csrfmiddlewaretoken:csrf_token, }, success:function (res) { console.log(res) if(res.code===0){ var data = res.data //先計算原來.comment-list的後代有幾個div,即有幾個人評論,然後再加一 var num = $(.comment-list>div).length + 1; //創建評論成功後,通過js在評論區列表在添加一條評論 var commenthtml = ` <div href="#" class="list-group-item"> <h4 class="list-group-item-heading comment-header"> <span>#${ num }樓</span> <span>${ data.create_time }</span> <span>${ data.username }</span> <span></span> </h4> <p class="list-group-item-text">${ data.content }</p> </div> ` //在原來的評論列表後面添加一條 $(.comment-list).append(commenthtml) //情空 textarea $(#new-comment).val(‘‘) } } }) })

上面寫的是添加父評論。

接下來添加子評論,就是回復的部分了:

博客園中當我們點擊回復按鈕時,評論區會顯示這樣:

分為兩部:當我們點擊回復標簽時,光標就會聚焦在評論區,並且出現了@xxx

技術分享圖片這個如何實現呢?

$(.replay).click(function () {
        //光標聚焦在textarea
        //出現@xxx
        var replayname = $(this).prev().text() //取到當前標簽的前一個標簽的文本
        $(#new-comment).focus().val(@+replayname+\n)
    })

comment表裏有parent_comment字段,我們應該怎麽取到這個字段的id呢?

當我們點擊提交的時候,我們不管有沒有父評論都給後端傳一個parent_id,我們給評論的那個div加一個自定義的id

<div href="#" class="list-group-item" my_id="{{ comment.id }}">
<!--評論展示區 開始-->
    <h4>評論</h4>
    <div class="list-group comment-list">
        {% for comment in comment_list %}
            <div href="#" class="list-group-item" my_id="{{ comment.id }}">
                <h4 class="list-group-item-heading comment-header">
                    <span>#{{ forloop.counter }}樓</span>
                    <span>{{ comment.create_time|date:Y-m-d H:i }}</span>
                    <span>{{ comment.user.username }}</span>
                    <span></span>
                </h4>
          {% if comment.parent_comment %}
<span style="display: block">@{{ comment.parent_comment.user.username }}</span>
<p class="list-group-item-text well">
{{ comment.parent_comment.content }}
</p>
{% endif %} <p class="list-group-item-text">{{ comment.content }}</p> </div> {% endfor %} </div> <!--評論展示區 結束-->

那麽我們如何區分誰是父評論誰是子評論呢?當我們點擊回復的時候,產生的評論就是子評論,直接點擊提交就是父評論,那我們應該怎麽操作呢?

我們把這個自定義的id添加到提交的按鈕上,當我們點擊提交的按鈕是進行判斷,如果這個id存在,就說明是子評論,否則就是父評論。

  //點擊回復按鈕時的綁定事件
$(.replay).click(function () { //光標聚焦在textarea //出現@xxx var replayname = $(this).prev().text() ;//取到當前標簽的前一個標簽的文本 $(#new-comment).focus().val(@+replayname+\n); //把當前評論的id值,存到提交的按鈕中 var pID = $(this).parent().parent().attr(my-id); $(#submit-comment).data(pid,pID) })
 //給評論按鈕綁定點擊事件
    $(#submit-comment).click(function () {
        var userid = {{ request.user.id }};
        var articleid = {{ article_obj.id }};
        var content = $(#new-comment).val();
        var csrf_token = $([name="csrfmiddlewaretoken"]).val();
        var parentId = $(this).data(pid) || ‘‘;
        if(parentId){
        //因為在添加自評論時,會出現@xxx的東西所以我們按照索引把他去除 content
= content.slice(content.indexOf(\n)+1,); } $.ajax({ url:/comment/, type:post, data:{ parent_id:parentId, user_id:userid, article_id:articleid, content:content, csrfmiddlewaretoken:csrf_token, }, success:function (res) { console.log(res) if(res.code===0){ var data = res.data //先計算原來.comment-list的後代有幾個div,即有幾個人評論,然後再加一 var num = $(.comment-list>div).length + 1; //創建評論成功後,通過js在評論區列表在添加一條評論 var commenthtml = ` <div href="#" class="list-group-item"> <h4 class="list-group-item-heading comment-header"> <span>#${ num }樓</span> <span>${ data.create_time }</span> <span>${ data.username }</span> <span></span> </h4> <p class="list-group-item-text">${ data.content }</p> </div> `; //在原來的評論列表後面添加一條 $(.comment-list).append(commenthtml); //情空 textarea $(#new-comment).val(‘‘);
            //當我們點擊提交按鈕時,應該把自定義的id去掉,不然下一次點擊提交的時候,還是有這個id值 $(
"#submit-comment").removeData("pid"); } } }) });

我們後端也能拿到parent_id。

def comment(request):
    if request.method == POST:
        res = {code:0}
        user_id = request.POST.get(user_id)
        article_id = request.POST.get(article_id)
        content = request.POST.get(content)
        parent_id = request.POST.get("parent_id")
        #創建評論
        with transaction.atomic():
            # 1.先去創建新評論
            if parent_id:
                #添加自評論
                comment_obj = models.Comment.objects.create(user_id=user_id, article_id=article_id, content=content,parent_comment_id=parent_id)
            else:
                #添加父評論
                comment_obj = models.Comment.objects.create(user_id=user_id, article_id=article_id, content=content)
            # 2.再去更新該文章的評論數
            models.Article.objects.update(comment_count=F(comment_count)+1)
        res[data] = {
            parent_id:parent_id,
            id:comment_obj.id,
            username:comment_obj.user.username,
            content:comment_obj.content,
            create_time:comment_obj.create_time.strftime("Y-m-d")
        }
        return JsonResponse(res)

這樣添加的評論還不夠完美,就是添加的評論沒有回復這個功能,

 //給評論按鈕綁定點擊事件
    $(#submit-comment).click(function () {
        var userid = {{ request.user.id }};
        var articleid = {{ article_obj.id }};
        var content = $(#new-comment).val();
        var csrf_token = $([name="csrfmiddlewaretoken"]).val();
        var parentId = $(this).data(pid) || ‘‘;
        if(parentId){
            //因為在添加自評論時,會出現@xxx的東西所以我們按照索引把他去除
            content = content.slice(content.indexOf(\n)+1,);
        }
        $.ajax({
            url:/comment/,
            type:post,
            data:{
                parent_id:parentId,
                user_id:userid,
                article_id:articleid,
                content:content,
                csrfmiddlewaretoken:csrf_token,
            },
            success:function (res) {
                console.log(res)
                if(res.code===0){
                    var data = res.data
                    //先計算原來.comment-list的後代有幾個div,即有幾個人評論,然後再加一
                    var num = $(.comment-list>div).length + 1;
                    //創建評論成功後,通過js在評論區列表在添加一條評論
                    var commenthtml = `
                    <div href="#" class="list-group-item">
                <h4 class="list-group-item-heading comment-header">
                    <span>#${ num }樓</span>
                    <span>${ data.create_time }</span>
                    <span>${ data.username }</span>
                    <span class="pull-right replay">回復</span>
                </h4>
                    <p class="list-group-item-text">${ data.content }</p>
                </div>
                    `;
                    //在原來的評論列表後面添加一條
                    $(.comment-list).append(commenthtml);
                    //情空 textarea
                    $(#new-comment).val(‘‘);
                    //當我們點擊提交按鈕時,應該把自定義的id去掉,不然下一次點擊提交的時候,還是有這個id值
                    $("#submit-comment").removeData("pid");
                }
            }
        })
    });

添加回復這個按鈕後,不能實現點擊,這就用到了事件委托,當一個事件不能完成時,我們把它委托給他的父標簽做將來的動作。

    //給回復按鈕綁定事件
    //$(.replay).click(function () {
    //事件委托,
     $(.comment-list).on(click,.replay,function () {
        //光標聚焦在textarea
        //出現@xxx
        var replayname = $(this).prev().text() ;//取到當前標簽的前一個標簽的文本
        $(#new-comment).focus().val(@+replayname+\n);
        //把當前評論的id值,存到提交的按鈕中
        var pID = $(this).parent().parent().attr(my-id);
        $(#submit-comment).data(pid,pID)
    })

這就是完整的代碼了

不懂得地方看視頻,(看視頻day79. 004)

上面設計到的知識點,jquery中的data方法,就是我們可以給任意一個jQuery對象添加一個值。

技術分享圖片

js中的三元運算:

技術分享圖片

還有就是事件委托

django框架之BBS項目之評論功能