文章詳情頁文章評論功能

1、文章評論功能實現流程  

  文章評論包含兩種評論,根評論:對文章的評論;子評論:對評論的評論。二者的區別在因而否存在父評論。javascript

  實現流程:一、構建樣式;二、提交根評論;三、顯示根評論(分爲render顯示和Ajax顯示);四、提交子評論;五、顯示子評論(分爲render顯示和Ajax顯示);六、評論樹顯示(博客園是樓層顯示)。css

2、構建評論樣式

一、article_detail.html:html

{# 文章點贊,清除浮動 #}
<div class="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="diggnum" 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>

<div class="comments">
    <p>發表評論</p>
    <p>暱稱: <input type="text" id="tbCommentAuthor" class="author" disabled="disabled" size="50" value="{{ request.user.username }}"></p>
    <p>評論內容</p>
    <textarea name="" id="" cols="60" rows="10"></textarea> {# textarea是一個內聯標籤 #}
    <p><button class="btn btn-default comment_btn">提交評論</button></p>
</div>

  因爲id=div_digg中存在浮動,顯示時會形成點贊區域和評論區域在同一行,在點贊區外包了一層div標籤,添加bootstrap的clearfix清除浮動。java

  因爲textarea是一個內聯標籤,下面直接接button標籤,文本框和按鈕會顯示在同一行,一樣給按鈕包一層p標籤,讓按鈕在文本框下方顯示。python

二、article_detail.css評論區樣式調整git

/* 評論 */
input.author {
    background-image: url("/static/font/icon_form.gif");
    background-repeat: no-repeat;
    border: 1px solid #ccc;
    padding: 4px 4px 4px 30px;
    width: 300px;
    font-size: 13px;
    background-position: 3px -3px;
}

三、顯示效果以下所示:ajax

  

3、提交根評論

一、建立評論路由數據庫

urlpatterns = [
    ...
    path('digg/', views.digg),  # 點贊
    path('comment/', views.comment),  # 評論
    ...
]

二、建立評論視圖函數commentdjango

def comment(request):
    print(request.POST)

    article_id = request.POST.get("article_id")
    pid = request.POST.get("pid")
    content = request.POST.get("content")
    user_id = request.user.pk

    # 在數據庫生成一條評論對象  父評論爲空是根評論,不爲空則是子評論
    comment_obj = models.Comment.objects.create(user_id=user_id, article_id=article_id, content=content, parent_comment_id=pid)

    return HttpResponse("comment")

三、在article_detail.html模板中建立評論事件json

<script>
    // 點贊請求
    $('#div_digg .action').click(function () {
    ...

    // 評論請求
    $(".comment_btn").click(function () {
        var content = $('#comment_content').val();    // 拿到評論框的內容
        var pid = "";   // 父評論默認爲空

        $.ajax({
            url: "/comment/",
            type: "post",
            data: {
                'csrfmiddlewaretoken': $("[name= 'csrfmiddlewaretoken']").val(),
                'article_id': "{{ article_obj.pk }}",
                'content': content,
                'pid': pid,
            },
            success:function (data) {
                console.log(data);

                // 提交後清空評論框
                $("#comment_content").val("");
            }
        })
    })
</script>

四、提交評論後,在數據庫blog_comment表中能夠看到評論記錄

  

4、顯示根評論

一、render顯示根評論

(1)要render顯示評論,須要修改article_detail視圖函數

def article_detail(request, username, article_id):

    user = UserInfo.objects.filter(username=username).first()
    blog = user.blog

    article_obj = models.Article.objects.filter(pk=article_id).first()

    comment_list = models.Comment.objects.filter(article_id=article_id)

    return render(request, "article_detail.html", locals())

  傳遞comment_list到模板中,根據過濾條件獲取的是當前文章的評論。

(2)在article_detail.html中構建評論列表

{# 文章評論列表 #}
<div class="comments">
    <p>評論列表</p>
    <ul class="list-group comment_list">
        {% for comment in comment_list %}
            <li class="list-group-item">
                <div>
                    <a href=""># {{ forloop.counter }}樓</a>    
                    <span>{{ comment.create_time|date:"Y-m-d H:i" }}</span>    
                    <a href=""><span><{{ comment.user.username }}/span></a>    
                    <a href="" class="pull-right">回覆</a>
                </div>
                <div class="comment_con">
                    {# 評論內容 #}
                    <p>{{ comment.content }}</p>
                </div>
            </li>
        {% endfor %}
    </ul>

    <p>發表評論</p>
    <p>暱稱: <input type="text" id="tbCommentAuthor" class="author" disabled="disabled" size="50" value="{{ request.user.username }}"></p>
    <p>評論內容</p>
    <textarea name="" id="comment_content" cols="60" rows="10"></textarea> {# textarea是一個內聯標籤 #}
    <p><button class="btn btn-default comment_btn">提交評論</button></p>
</div>

  1)利用bootstrap的列表組組件來構建文章列表樣式。

  2)修飾評論內容樣式article_detail.css:

.comment_con {
    margin-top: 10px;
}

(3)顯示效果

  

二、Ajax顯示根評論

 (1)更新comment視圖函數,準備返回給ajax回調函數處理的數據

def comment(request):
    print(request.POST)

    article_id = request.POST.get("article_id")
    pid = request.POST.get("pid")
    content = request.POST.get("content")
    user_id = request.user.pk

    # 在數據庫生成一條評論對象  父評論爲空是根評論,不爲空則是子評論
    comment_obj = models.Comment.objects.create(user_id=user_id, article_id=article_id, content=content, parent_comment_id=pid)

    response = {}
    # create_time是一個datetime.datetime對象,在json序列化時不能對對象進行json序列化,必須進行strftime的轉換
    response["create_time"] = comment_obj.create_time.strftime("%Y-%m-%d %X")
    # 評論人
    response["username"] = request.user.username
    # 內容
    response["content"] = content

    return JsonResponse(response)

(2)回調函數處理數據,顯示新的評論

// 評論請求
$(".comment_btn").click(function () {
    var content = $('#comment_content').val();    // 拿到評論框的內容
    var pid = "";   // 父評論默認爲空

    $.ajax({
        url: "/comment/",
        type: "post",
        data: {
            'csrfmiddlewaretoken': $("[name= 'csrfmiddlewaretoken']").val(),
            'article_id': "{{ article_obj.pk }}",
            'content': content,
            'pid': pid,
        },
        success: function (data) {
            console.log(data);

            // 獲取視圖函數返回的數據
            var create_time = data.create_time;
            var username = data.username;
            var content = data.content;

            // ES6特性:字符串模板。
            // ES6中容許使用反引號 ` 來建立字符串,此種方法建立的字符串裏面能夠包含由美圓符號加花括號包裹的變量${vraible}。
            var s = `
                <li class="list-group-item">
                    <div>
                        <span>${create_time}</span>    
                        <a href=""><span><${username}/span></a>    
                        <a href="" class="pull-right">回覆</a>
                    </div>
                    <div class="comment_con">
                        {# 評論內容 #}
                        <p>${content}</p>
                    </div>
                </li>`;
            // DOM操做把標籤字符串整個放入ul的標籤中去
            $("ul.comment_list").append(s);

            // 提交後清空評論框
            $("#comment_content").val("");
        }
    })
})

注意:1)ES6中容許使用反引號 ` 來建立字符串,此種方法建立的字符串裏面能夠包含由美圓符號加花括號包裹的變量${vraible}。

//產生一個隨機數
var num=Math.random();
//將這個數字輸出到console
console.log(`your num is ${num}`);

  2)獲取視圖函數返回的數據:

// 獲取視圖函數返回的數據
var create_time = data.create_time;
var username = data.username;
var content = data.content;

(3)顯示效果以下:

  

 

 5、提交子評論

   經過點擊評論後的回覆按鈕來提交子評論,點擊回覆按鈕事件:光標挪到輸出框下;輸出框顯示 @用戶名。

一、修改article_detail.html文章評論列表回覆標籤

{# 文章評論列表 #}
<div class="comments">
    <p>評論列表</p>
    <ul class="list-group comment_list">
        {% for comment in comment_list %}
            <li class="list-group-item">
                <div>
                    <a href=""># {{ forloop.counter }}樓</a>   
                    <span>{{ comment.create_time|date:"Y-m-d H:i" }}</span>   
                    <a href=""><span><{{ comment.user.username }}/span></a>   
                    <a class="pull-right reply_btn" username="{{ comment.user.username }}">回覆</a>
                </div>
                <div class="comment_con">
                    {# 評論內容 #}
                    <p>{{ comment.content }}</p>
                </div>
            </li>
        {% endfor %}
    </ul>

    <p>發表評論</p>
    <p>暱稱: <input type="text" id="tbCommentAuthor" class="author" disabled="disabled" size="50"
                  value="{{ request.user.username }}"></p>
    <p>評論內容</p>
    <textarea name="" id="comment_content" cols="60" rows="10"></textarea> {# textarea是一個內聯標籤 #}
    <p>
        <button class="btn btn-default comment_btn">提交評論</button>
    </p>
</div>

  給<a class="pull-right reply_btn" username="{{ comment.user.username }}">回覆</a>,添加了屬性username,拿到當前行評論的用戶。

  注意:回覆這個a標籤不能添加href=""屬性。不然在觸發回覆事件後,會刷新當前頁面

二、在article_detail.html中編輯點擊回覆按鈕事件

// 回覆按鈕事件
$(".reply_btn").click(function () {
    $('#comment_content').focus();   // 獲取焦點
    // 拿到對應的父評論的用戶名
    var val = "@" + $(this).attr("username")+"\n";
    // 給輸入框賦值
    $('#comment_content').val(val);

});

  顯示效果以下所示:

  

 三、提交子評論

article_detail.html作以下處理

<script>
    // 點贊請求
    ...
    
    var pid = "";   // 父評論默認爲空
    // 評論請求
    $(".comment_btn").click(function () {
        var content = $('#comment_content').val();    // 拿到評論框的內容
        if (pid) {
            // pid有值,是子評論
            // 處理拿到子評論值方法一:
            var index = content.indexOf("\n");  // 拿到換行符索引值
            content = content.slice(index+1);  // 切片處理,從index+1一直取到最後
        }

        $.ajax({
            url: "/comment/",
            type: "post",
            data: {
                'csrfmiddlewaretoken': $("[name= 'csrfmiddlewaretoken']").val(),
                'article_id': "{{ article_obj.pk }}",
                'content': content,
                'pid': pid,
            },
            success: function (data) {
                console.log(data);

                // 獲取視圖函數返回的數據
                var create_time = data.create_time;
                var username = data.username;
                var content = data.content;

                // ES6特性:字符串模板。
                // ES6中容許使用反引號 ` 來建立字符串,此種方法建立的字符串裏面能夠包含由美圓符號加花括號包裹的變量${vraible}。
                var s = `
            <li class="list-group-item">
                <div>
                    <span>${create_time}</span>    
                    <a href=""><span><${username}/span></a>    
                    <a href="" class="pull-right">回覆</a>
                </div>
                <div class="comment_con">
                    {# 評論內容 #}
                    <p>${content}</p>
                </div>
            </li>`;
                // DOM操做把標籤字符串整個放入ul的標籤中去
                $("ul.comment_list").append(s);

                // 提交後清空評論框
                $("#comment_content").val("");
                // pid從新賦值
                pid = "";
            }
        })
    });

    // 回覆按鈕事件
    $(".reply_btn").click(function () {
        $('#comment_content').focus();   // 獲取焦點
        // 拿到對應的父評論的用戶名
        var val = "@" + $(this).attr("username")+"\n";
        // 給輸入框賦值
        $('#comment_content').val(val);
        // 拿到父評論的主鍵值
        pid = $(this).attr("comment_pk");
    });
</script>

注意:

(1)將var pid="";  改成全局變量,拿到事件外。根據pid是否有值,處理評論框內容:

  pid沒有值的時候:

var content = $('#comment_content').val();

  pid有值的時候:

if (pid) {
    // pid有值,是子評論
    // 處理拿到子評論值方法一:
    var index = content.indexOf("\n");  // 拿到換行符索引值
    content = content.slice(index+1);  // 切片處理,從index+1一直取到最後
}

(2)給回覆按鈕這個a標籤添加一個新的自定義屬性:comment_pk。

<a class="pull-right reply_btn" username="{{ comment.user.username }}" comment_pk="{{ comment.pk }}">回覆</a>

  點擊回覆按鈕拿到父評論的主鍵值

// 拿到父評論的主鍵值
pid = $(this).attr("comment_pk");

(3)每次提交評論後,都要清空pid。這樣發佈子評論後,不刷新頁面緊接着又發佈評論,這個新評論就不會有父評論了,

(4)查看數據庫中的blog_comment表,子評論的父評論id:

  

6、顯示子評論

一、render顯示子評論

{# 文章評論列表 #}
<div class="comments">
    <p>評論列表</p>
    <ul class="list-group comment_list">
        {% for comment in comment_list %}
            <li class="list-group-item">
                <div>
                    <a href=""># {{ forloop.counter }}樓</a>   
                    <span>{{ comment.create_time|date:"Y-m-d H:i" }}</span>   
                    <a href=""><span><{{ comment.user.username }}/span></a>   
                    <a class="pull-right reply_btn" username="{{ comment.user.username }}" comment_pk="{{ comment.pk }}">回覆</a>
                </div>

                {% if comment.parent_comment_id %}
                    <div class="pid_info well">
                        <p>
                            {# 拿到父評論對象評論人和評論內容 #}
                            {{ comment.parent_comment.user.username }}: {{ comment.parent_comment.content }}
                        </p>
                    </div>
                {% endif %}

                <div class="comment_con">
                    {# 評論內容 #}
                    <p>{{ comment.content }}</p>
                </div>
            </li>
        {% endfor %}
    </ul>

    <p>發表評論</p>
    <p>暱稱: <input type="text" id="tbCommentAuthor" class="author" disabled="disabled" size="50"
                  value="{{ request.user.username }}"></p>
    <p>評論內容</p>
    <textarea name="" id="comment_content" cols="60" rows="10"></textarea> {# textarea是一個內聯標籤 #}
    <p>
        <button class="btn btn-default comment_btn">提交評論</button>
    </p>
</div>

(1)在模板中判斷評論對象是否有父評論,若是有顯示父評論人和評論對象:

{% if comment.parent_comment_id %}
    <div class="pid_info well">
        <p>
            {# 拿到父評論對象評論人和評論內容 #}
            {{ comment.parent_comment.user.username }}: {{ comment.parent_comment.content }}
        </p>
    </div>
{% endif %}

(2)bootstrap的well用在元素上,就能有嵌入(inset)的簡單效果。

(3)顯示效果:

  

二、Ajax顯示子評論

 (1)改寫article.detail.html評論請求事件回調函數事件處理:

var pid = "";   // 父評論默認爲空
// 評論請求
$(".comment_btn").click(function () {
    var content = $('#comment_content').val();    // 拿到評論框的內容
    if (pid) {
        // pid有值,是子評論
        // 處理拿到子評論值方法一:
        var index = content.indexOf("\n");  // 拿到換行符索引值
        content = content.slice(index + 1);  // 切片處理,從index+1一直取到最後
    }

    $.ajax({
        url: "/comment/",
        type: "post",
        data: {
            'csrfmiddlewaretoken': $("[name= 'csrfmiddlewaretoken']").val(),
            'article_id': "{{ article_obj.pk }}",
            'content': content,
            'pid': pid,
        },
        success: function (data) {
            console.log(data);

            // 獲取視圖函數返回的數據
            var create_time = data.create_time;
            var username = data.username;
            var content = data.content;

            var parent_username = data.parent_username;
            var parent_content = data.parent_content;

            if (pid) {
                // ES6特性:字符串模板。
                // ES6中容許使用反引號 ` 來建立字符串,此種方法建立的字符串裏面能夠包含由美圓符號加花括號包裹的變量${vraible}。
                var s = `
                    <li class="list-group-item">
                        <div>
                            <span>${create_time}</span>    
                            <a href=""><span><${username}/span></a>    
                            <a href="" class="pull-right">回覆</a>
                        </div>
                        <div class="pid_info well">
                            <p>
                                {# 拿到父評論對象評論人和評論內容 #}
                                ${ parent_username }: ${ parent_content }
                            </p>
                        </div>
                        <div class="comment_con">
                            {# 評論內容 #}
                            <p>${content}</p>
                        </div>
                    </li>`;
            } else {
                var s = `
                    <li class="list-group-item">
                        <div>
                            <span>${create_time}</span>    
                            <a href=""><span><${username}/span></a>    
                            <a href="" class="pull-right">回覆</a>
                        </div>
                        <div class="comment_con">
                            {# 評論內容 #}
                            <p>${content}</p>
                        </div>
                    </li>`;
            }
            // DOM操做把標籤字符串整個放入ul的標籤中去
            $("ul.comment_list").append(s);

            // 提交後清空評論框
            $("#comment_content").val("");
            // pid從新賦值
            pid = "";
        }
    })
});

  注意:根據pid是否有值對var s的標籤字符串構建不一樣的結構和樣式。也就是針對根評論和子評論構建不一樣的結構。因爲構建子評論須要顯示父評論的用戶名和評論內容。所以須要在視圖函數中返回響應的數據。

(2)在comment視圖函數中添加父評論對象用戶名和評論內容

def comment(request):
    print(request.POST)

    article_id = request.POST.get("article_id")
    pid = request.POST.get("pid")
    content = request.POST.get("content")
    user_id = request.user.pk

    # 在數據庫生成一條評論對象  父評論爲空是根評論,不爲空則是子評論
    comment_obj = models.Comment.objects.create(user_id=user_id, article_id=article_id, content=content, parent_comment_id=pid)

    response = {}
    # create_time是一個datetime.datetime對象,在json序列化時不能對對象進行json序列化,必須進行strftime的轉換
    response["create_time"] = comment_obj.create_time.strftime("%Y-%m-%d %X")
    # 評論人
    response["username"] = request.user.username
    # 內容
    response["content"] = content

    # 父評論對象評論人和評論內容
    response["parent_username"] = comment_obj.parent_comment.user.username
    response["parent_content"] = comment_obj.parent_comment.content

    return JsonResponse(response)

(3)顯示效果:

  

7、評論樹

  前面都是用的評論樓來展現評論列表,可是這種方式結構不夠清晰。所以也須要學會用評論樹來展現評論列表。

一、評論樹的請求數據

(1)構建article_detail.html中評論樹標籤和點擊事件

{# 文章評論列表 #}
<div class="comments list-group">
    <p class="tree_btn">評論樹</p>
    <div class="comment_tree">

    </div>
    <script>
        $(".tree_btn").click(function () {
            $.ajax({
                url: "/get_comment_tree/",
                type: "get",
                data: {
                    article_id: "{{ article_obj.pk }}",

                },
                success: function (data) {
                    console.log(data);
                }
            })
        })
    </script>
    <p>評論列表</p>
    ...
</div>

(2)根據ajax的請求url創建對應的url

urlpatterns = [
    path('admin/', admin.site.urls),
    ...
    path('comment/', views.comment),  # 評論
    path("get_comment_tree/", views.get_comment_tree),   # 評論樹
    ...
]

(3)創建對應的視圖函數get_comment_tree

def get_comment_tree(request):
    article_id = request.GET.get("article_id")
    # 過濾出文章對應的評論,挑出主鍵值、評論內容、父評論id,拿到的是一個queryset,結構相似一個列表裏面裝着一個個字典
    # 可是queryset並非一個列表,能夠用list()函數將其轉換爲列表
    ret = list(models.Comment.objects.filter(article_id=article_id).values("pk", "content", "parent_comment_id"))

    # JsonResponse對非字典的數據進行序列化,必須設置一個參數safe=False
    return JsonResponse(ret, safe=False)

  注意:QuerySet雖然結構相似一個列表裏面裝着一個個字典但並非一個列表。能夠用list()函數轉換爲列表。

  其次通常JsonResponse都是用來對字典進行序列化,若是要對一個非字典的數據序列化,必須設置一個參數safe=False,不然會報錯。

(4)訪問頁面點擊頁面中評論樹時,頁面控制檯輸出了一條數組數據:

  

二、展開評論樹

{# 文章評論列表 #}
    <div class="comments list-group">
        <p class="tree_btn">評論樹</p>
        <div class="comment_tree">

        </div>
        <script>
            $(".tree_btn").click(function () {
                $.ajax({
                    url: "/get_comment_tree/",
                    type: "get",
                    data: {
                        article_id: "{{ article_obj.pk }}",

                    },
                    success: function (data) {
                        console.log(data);  // data是一個列表,列表中包含一個個字典
                        $.each(data, function (index, comment_object) {
                            var pk = comment_object.pk;
                            var content = comment_object.content;
                            var parent_comment_id = comment_object.parent_comment_id;

                            var s = '<div class="comment_item" comment_id='+pk+' style="margin-left: 20px"><span>'+content+'</span></div>';

                            // 判斷評論是根評論仍是子評論
                            if (!parent_comment_id) {    // 感嘆號取反
                                // 根評論
                                $(".comment_tree").append(s);
                            } else {
                                // 子評論
                                // 放入父評論的div標籤
                                // 屬性選擇器,找到comment_id屬性值對應的div
                                $("[comment_id="+parent_comment_id+"]").append(s);
                            }

                        })
                    }
                })
            })
        </script>
    ...
</div>

  注意:

(1)變量s是將要插入模板中的標籤字符串,標籤字符串內定義的評論的內容。

<div class="comment_item" comment_id='pk' style="margin-left: 20px">
    <span>評論內容</span>
</div>

  每一個標籤字符串都定義了comment_id,對應評論對象的主鍵值。定義樣式,則是爲了子評論相對父評論向左移動20像素,樣式獲得錯開。

(2)在ajax回調函數循環get_comment_tree視圖函數返回列表

success: function (data) {
    console.log(data);  // data是一個列表,列表中包含一個個字典
    $.each(data, function (index, comment_object) {
        var pk = comment_object.pk;
        var content = comment_object.content;
        var parent_comment_id = comment_object.parent_comment_id;   
     ...
   })
}    

(3)若是判斷是子評論的話,須要利用屬性選擇器,找到屬性comment_id=父評論的id,這樣篩選到對應的評論將標籤字符串放如對應的div中。

var s = '<div class="comment_item" comment_id='+pk+' style="margin-left: 20px"><span>'+content+'</span></div>';

// 判斷評論是根評論仍是子評論
if (!parent_comment_id) {    // 感嘆號取反
    // 根評論
    $(".comment_tree").append(s);
} else {
    // 子評論
    // 放入父評論的div標籤
    // 屬性選擇器,找到comment_id屬性值對應的div
    $("[comment_id="+parent_comment_id+"]").append(s);
}

(4)顯示效果:

  

三、評論樹回顧和優化

(1)在視圖中獲取的評論列表,會不會有子評論在前,父評論在後,致使在回調函數中循環列表時,發現有子評論沒法找到父評論沒法插入?

答:這個問題是不會發生的,由於評論生成是按主鍵進行排序的,根評論必定在前,與它關聯的子評論必定在後。

  爲了保險在獲取評論對象時仍是order_by作一下排序:

def get_comment_tree(request):
    article_id = request.GET.get("article_id")
    ret = list(models.Comment.objects.filter(article_id=article_id).order_by("pk").values("pk", "content", "parent_comment_id"))

    return JsonResponse(ret, safe=False)

(2)再也不經過點擊評論樹標籤,觸發事件來顯示評論樹。直接訪問文章頁面就顯示評論樹?

答:再也不把ajax事件內嵌在click事件中,瀏覽器加載標籤字符串時就會直接執行ajax,馬上發新的請求拿到響應結果,因爲速度很是快,馬上就將評論樹顯示在頁面上。

<script>
    // $(".tree_btn").click(function () {
    $.ajax({
        url: "/get_comment_tree/",
        type: "get",
        data: {
            article_id: "{{ article_obj.pk }}",

        },
        success: function (data) {
            console.log(data);  // data是一個列表,列表中包含一個個字典
            $.each(data, function (index, comment_object) {
                var pk = comment_object.pk;
                var content = comment_object.content;
                var parent_comment_id = comment_object.parent_comment_id;

                var s = '<div class="comment_item" comment_id='+pk+' style="margin-left: 20px"><span>'+content+'</span></div>';

                // 判斷評論是根評論仍是子評論
                if (!parent_comment_id) {    // 感嘆號取反
                    // 根評論
                    $(".comment_tree").append(s);
                } else {
                    // 子評論
                    // 放入父評論的div標籤
                    // 屬性選擇器,找到comment_id屬性值對應的div
                    $("[comment_id="+parent_comment_id+"]").append(s);
                }

            })
        }
    })
    // })
</script>

8、事務操做

  好比在數據庫生成一條評論對象,同時將文章的評論數進行更新。這兩步操做須要設計爲同進同退,即若是有一步沒完成,完成的操做須要在數據庫回退。

def comment(request):
    print(request.POST)

    article_id = request.POST.get("article_id")
    pid = request.POST.get("pid")
    content = request.POST.get("content")
    user_id = request.user.pk

    # 事務操做:生成記錄和評論數更新同進同退
    with transaction.atomic():
        # 在數據庫生成一條評論對象  父評論爲空是根評論,不爲空則是子評論
        comment_obj = models.Comment.objects.create(user_id=user_id, article_id=article_id, content=content, parent_comment_id=pid)
        # 文章評論數更新
        models.Article.objects.filter(pk=article_id).update(comment_count=F("comment_count")+1)

    response = {}
    # create_time是一個datetime.datetime對象,在json序列化時不能對對象進行json序列化,必須進行strftime的轉換
    response["create_time"] = comment_obj.create_time.strftime("%Y-%m-%d %X")
    # 評論人
    response["username"] = request.user.username
    # 內容
    response["content"] = content

    # 父評論對象評論人和評論內容
    response["parent_username"] = comment_obj.parent_comment.user.username
    response["parent_content"] = comment_obj.parent_comment.content

    return JsonResponse(response)

一、引入如下模塊來實現事務操做:

from django.db import transaction

二、將幾個操做做爲一個事務的方法:

# 事務操做:生成記錄和評論數更新同進同退
with transaction.atomic():
    # 在數據庫生成一條評論對象  父評論爲空是根評論,不爲空則是子評論
    comment_obj = models.Comment.objects.create(user_id=user_id, article_id=article_id, content=content, parent_comment_id=pid)
    # 文章評論數更新          
    models.Article.objects.filter(pk=article_id).update(comment_count=F("comment_count")+1)

9、發送郵件

  文章被評論了,給做者發一份站內信通知他多了一條評論。這裏使用django提供的模塊來實現發送郵件:

from django.core.mail import send_mail

一、send_mail函數的源碼分析

def send_mail(subject, message, from_email, recipient_list,
              fail_silently=False, auth_user=None, auth_password=None,
              connection=None, html_message=None):
    """

    :param subject: 標題
    :param message: 郵件內容
    :param from_email: 發件郵箱
    :param recipient_list: 收件郵箱
    :param fail_silently:
    :param auth_user:
    :param auth_password:
    :param connection:
    :param html_message:
    :return:
    """
    connection = connection or get_connection(
        username=auth_user,
        password=auth_password,
        fail_silently=fail_silently,
    )
    mail = EmailMultiAlternatives(subject, message, from_email, recipient_list, connection=connection)
    if html_message:
        mail.attach_alternative(html_message, 'text/html')

    return mail.send()

二、settings配置官方郵箱

# 郵箱配置
EMAIL_HOST = 'smtp.163.com'  # 若是是 qq 改爲 smtp.exmail.qq.com
EMAIL_PORT = 465  # qqs是465
EMAIL_HOST_USER = 'xxx@163.com'           # 賬號
EMAIL_HOST_PASSWORD = 'xxxxxx'  # 密碼(受權碼)
# DEFAULT_FROM_EMAIL = EMAIL_HOST_USER    # 默認使用當前配置的user
EMAIL_USE_SSL = True  # 是否使用SSL證書, 網易郵箱關閉SSL後SMTP應該爲25

三、在comment視圖中配置郵件發送

def comment(request):
    ...
    # 爲了發送郵件拿到文章對象
    article_obj = models.Article.objects.filter(pk=article_id).first()
    ...

    # 發送郵件
    from django.core.mail import send_mail
    from cnblog import settings
    send_mail(
        "您的文章%s新增了一條評論內容" % article_obj.title,
        content,
        settings.EMAIL_HOST_USER,   # 發送方
        ["44xxxx@qq.com"]   # 接收方
    )
    ...

  接收的郵箱,應該是用戶註冊時填寫的郵箱信息。

  評論後,個人qq郵箱收到郵件:

  

四、多線程解決郵件發送網絡延遲引發的網頁卡頓

  點擊評論會發現,頁面須要卡好久,嚴重影響了用戶體驗。

# 發送郵件
from django.core.mail import send_mail
from cnblog import settings
# send_mail(
#     "您的文章%s新增了一條評論內容" % article_obj.title,
#     content,
#     settings.EMAIL_HOST_USER,   # 發送方
#     ["44xxxx@qq.com"]   # 接收方
# )

import threading

t = threading.Thread(target=send_mail, args=(
    "您的文章%s新增了一條評論內容" % article_obj.title,
    content,
    settings.EMAIL_HOST_USER,  # 發送方
    ["443xxxx@qq.com"]  # 接收方
))
t.start()

  這樣用一個線程去跑send_email,就不會影響響應結果反饋了。

五、改完後完整的comment視圖函數以下所示

def comment(request):
    print(request.POST)

    article_id = request.POST.get("article_id")
    pid = request.POST.get("pid")
    content = request.POST.get("content")
    user_id = request.user.pk

    # 爲了發送郵件拿到文章對象
    article_obj = models.Article.objects.filter(pk=article_id).first()

    # 事務操做:生成記錄和評論數更新同進同退
    with transaction.atomic():
        # 在數據庫生成一條評論對象  父評論爲空是根評論,不爲空則是子評論
        comment_obj = models.Comment.objects.create(user_id=user_id, article_id=article_id, content=content, parent_comment_id=pid)
        # 文章評論數更新
        models.Article.objects.filter(pk=article_id).update(comment_count=F("comment_count")+1)

    response = {}
    # create_time是一個datetime.datetime對象,在json序列化時不能對對象進行json序列化,必須進行strftime的轉換
    response["create_time"] = comment_obj.create_time.strftime("%Y-%m-%d %X")
    # 評論人
    response["username"] = request.user.username
    # 內容
    response["content"] = content

    # 發送郵件
    from django.core.mail import send_mail
    from cnblog import settings
    # send_mail(
    #     "您的文章%s新增了一條評論內容" % article_obj.title,
    #     content,
    #     settings.EMAIL_HOST_USER,   # 發送方
    #     ["443614404@qq.com"]   # 接收方
    # )

    import threading

    t = threading.Thread(target=send_mail, args=(
        "您的文章%s新增了一條評論內容" % article_obj.title,
        content,
        settings.EMAIL_HOST_USER,  # 發送方
        ["443614404@qq.com"]  # 接收方
    ))
    t.start()


    # 父評論對象評論人和評論內容
    response["parent_username"] = comment_obj.parent_comment.user.username
    response["parent_content"] = comment_obj.parent_comment.content

    return JsonResponse(response)
comment視圖函數
相關文章
相關標籤/搜索