文章點贊功能(Ajax)

1、文章點贊樣式構建

一、將base.html的css樣式改成外部引入

  將base.html的內嵌樣式刪除,改成使用 HTML 頭部的 <head> 標籤對中使用<link>標籤來引入外部的 CSS 文件。javascript

base.html內容以下所示:css

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <!-- 引入 Bootstrap 核心 CSS 文件 -->
    <link rel="stylesheet" href="/static/blog/bootstrap-3.3.7/css/bootstrap.css">
    <!-- jQuery (Bootstrap 的全部 JavaScript 插件都依賴 jQuery,因此必須放在前邊) -->
    <script src="/static/js/jquery-3.3.1.js"></script>
    <!-- 引入 Bootstrap 核心 JavaScript 文件 -->
    <script src="/static/blog/bootstrap-3.3.7/js/bootstrap.js"></script> <!--依賴jquery-->
    <link rel="stylesheet" href="/static/blog/css/home_site.css">
    <link rel="stylesheet" href="/static/blog/css/article_detail.css">
</head>
<body>

<div class="header">
    <div class="content">
        <!--站點標題-->
        <p class="title">
            <span>{{ blog.title }}</span>
            <a href="" class="backend">管理</a>
        </p>
    </div>
</div>

<div class="container">
    <div class="row">
        <div class="col-md-3">
            <!--添加bootstrap面板-->
            {% load my_tags %}
            {% get_classification_style username %}
        </div>
        <div class="col-md-9">
            {% block content %}

            {% endblock %}
        </div>
    </div>
</div>

</body>
</html>

 我的站點的樣式——home_site.css:html

* {
    margin: 0;
    padding: 0;
}
.header {
    width: 100%;
    height: 60px;
    background-color: #369;
}
.header .title {
    font-size: 18px; /* 字體大小 */
    font-weight: 100; /* 字體粗細 */
    line-height: 60px; /* 行高與頁頭一致,完成居中 */
    color: white;
    margin-left: 15px;
    margin-top: -10px;
}
.backend {
    float: right; /* 浮動到右邊 */
    color: white;
    text-decoration: none; /* 去除下劃線 */
    font-size: 16px;
    margin-right: 12px;
    margin-top: 10px;
}
.pub_info {
    margin-top: 10px;
    color: darkgray;
}
.menu {
    margin-top: 20px;
}

文章詳情頁的樣式——article_detail.css:java

.article_info .title {
    margin-bottom: 20px;
}

  上述css代碼是將標題部分和文字主體部分錯開20像素。python

二、構建點贊樣式

  根據博客園代碼,在article_detail.html引入文章推薦踩滅:jquery

{% extends "base.html" %}

{% block content %}
    <h3 class="text-center">{{ article_obj.title }}</h3>
    <div class="cont">
        {{ article_obj.content|safe }}
    </div>

    {# 文章點贊 #}
    <div id="div_digg">
        <div class="diggit">
            <span class="diggnum" id="digg_count">1</span>
        </div>
        <div class="buryit">
            <span class="diggnum" id="bury_count">0</span>
        </div>
        <div class="clear"></div>
        <div class="diggword" id="digg_tips" style="color: red;"></div>
    </div>
{% endblock %}

  將點讚的css樣式寫入article_detail.css中:git

.article_info .title {
    margin-bottom: 20px;
}

#div_digg {
    float: right;
    margin-bottom: 10px;
    margin-right: 30px;
    font-size: 12px;
    width: 125px;
    text-align: center;
    margin-top: 10px;
}

/* 推薦 */
.diggit {
    float: left;
    width: 46px;
    height: 52px;
    background: url('/static/font/upup.gif') no-repeat;
    text-align: center;
    cursor: pointer;
    margin-top: 2px;
    padding-top: 5px;
}

/* 反對 */
.buryit {
    float: right;
    margin-left: 20px;
    width: 46px;
    height: 52px;
    background: url('/static/font/downdown.gif') no-repeat;
    text-align: center;
    cursor: pointer;
    margin-top: 2px;
    padding-top: 5px;
}

.clear {
    clear: both;  /* 清除浮動,解決塌陷問題 */
}

  顯示效果:ajax

  

 

2、文章點贊事件綁定 (Ajax)

  給推薦和反對的這兩個標籤綁定事件,一點擊就發送ajax請求。數據庫

  另外查看blog_articleupdown表:django

  

  其中is_up字段是存的是一個布爾值。點擊推薦則爲True,點擊反對則爲False.在這裏將兩個標籤合在一個事件中,僅僅是作判斷點擊的是哪一個標籤。注意文章點讚的script代碼應寫在article_detail.html模板中。

  因爲這兩個標籤有不一樣的類,一個包含diggit,一個包含buryit。所以只須要判斷點擊的標籤class名就能夠,article_detail.html:

 {% extends "base.html" %}

{% block content %}
    <h3 class="text-center">{{ article_obj.title }}</h3>
    <div class="cont">
        {{ article_obj.content|safe }}
    </div>

    {# 文章點贊 #}
    <div id="div_digg">
        {# 推薦 #}
        <div class="diggit action">
            <span class="diggnum" id="digg_count">1</span>
        </div>
        {# 點滅 #}
        <div class="buryit action">
            <span class="diggnum" id="bury_count">0</span>
        </div>
        <div class="clear"></div>
        <div class="diggword" id="digg_tips" style="color: red;"></div>
    </div>

    <script>
        $('#div_digg .action').click(function () {
            var is_up = $(this).hasClass("diggit");
            alert(is_up);
        })
    </script>
{% endblock %}

3、文章點贊保存

一、在article_detail.html模板初步構建ajax請求

<script>
    $('#div_digg .action').click(function () {
        var is_up = $(this).hasClass("diggit");
        $.ajax({
            url: "/digg/",
            type: "post",
            data: {
                'csrfmiddlewaretoken': $("[name= 'csrfmiddlewaretoken']").val(),
                "is_up": is_up,
                "article_id": "{{ article_obj.pk }}",
            },
            success: function (data) {
                console.log(data);
            }
        })
    })
</script>

  須要注意:文章點贊人、評論人都應是當前登陸人。所以這裏不須要傳入user_id到ajax中。

  因爲這裏是post請求,所以須要添加csrf_token避免forbidden錯誤,所以須要構建一條新路由:

urlpatterns = [
    path('admin/', admin.site.urls),
    ...
    path('digg/', views.digg),  # 點贊
  ... ]

  建立點贊視圖函數:

def digg(request):
    """
    點贊視圖函數
    :param request:
    :return:
    """
    print(request.POST)

    return HttpResponse("OK")

  訪問頁面,點擊推薦,視圖函數request.POST輸出:

<QueryDict: {'csrfmiddlewaretoken': ['hBlBWfxGFhDXaqDCfkSMFhKd6ZhZsbuqM8TEj3upzwe2NynenybodHgQyFHQAvZ0'], 'is_up': ['true'], 'article_id': ['1']}>

  同時頁面控制檯輸出:OK。

二、生成贊記錄

import json

def digg(request):
    """
    點贊視圖函數
    :param request:
    :return:
    """
    print(request.POST)
    # <QueryDict: {'csrfmiddlewaretoken': ['hBlBWfxGFhDXaqDCfkSMFhKd6ZhZsbuqM8TEj3upzwe2NynenybodHgQyFHQAvZ0'],
    #               'is_up': ['true'], 'article_id': ['1']}>

    article_id = request.POST.get("article_id")
    # is_up = request.POST.get("is_up")    # 拿到的是一個字符串 "true"
    is_up = json.loads(request.POST.get("is_up"))   # 反序列化,拿到一個bool值

    # 點贊人即當前登陸人
    user_id = request.user.pk

    # 建立一條新記錄
    ard = models.ArticleUpDown.objects.create(user_id=user_id, article_id=article_id, is_up=is_up)

    return HttpResponse("OK")

在這裏須要注意:

(1)生成一條贊記錄根據models中的類:

  

  所以拿到article_id、user_id、is_up這三條就能夠生成一個新記錄。

(2)request.POST.get("is_up") 拿到的結果是一個字符串。由於它在js中雖然也是bool值,可是在傳遞時,沒有設置ContentType,默認使用urlencoded編碼,在組裝鍵值的時候,is_up = True就按照字符串發送出去了。所以須要json來進行反序列化。

(3)點擊贊後,查看blog_articleupdwon表記錄,確認新記錄是否生成:

  

4、文章點贊數的數據同步

  生成一條贊記錄,就應該把贊記錄對應的文章up_count+1,若是是踩滅,則將down_count+1;保持這種同步。

  涉及到up_count自加一,須要用到Django的F函數。

import json

from django.db.models import F   # F函數

def digg(request):
    """
    點贊視圖函數
    :param request:
    :return:
    """
    print(request.POST)
    # <QueryDict: {'csrfmiddlewaretoken': ['hBlBWfxGFhDXaqDCfkSMFhKd6ZhZsbuqM8TEj3upzwe2NynenybodHgQyFHQAvZ0'],
    #               'is_up': ['true'], 'article_id': ['1']}>

    article_id = request.POST.get("article_id")
    # is_up = request.POST.get("is_up")    # 拿到的是一個字符串 "true"
    is_up = json.loads(request.POST.get("is_up"))   # 反序列化,拿到一個bool值

    # 點贊人即當前登陸人
    user_id = request.user.pk

    # 建立一條新記錄
    ard = models.ArticleUpDown.objects.create(user_id=user_id, article_id=article_id, is_up=is_up)

    queryset = models.Article.objects.filter(pk=article_id)
    if is_up:
        queryset.update(up_count=F("up_count")+1)
    else:
        queryset.update(up_count=F("down_count")+1)

    return HttpResponse("OK")

  工做原理是,直接在數據庫中查出數據,計數後更改數據庫。點贊後刷新頁面,點贊次數已經更新:

  

5、點贊提示重複操做

  博客園文章點贊規則:用戶對一篇文章點贊後,不容許再對文章進行點贊或踩滅操做。

一、根據用戶在點贊是否有記錄進行判斷

import json
from django.http import JsonResponse

from django.db.models import F   # F函數

def digg(request):
    """
    點贊視圖函數
    :param request:
    :return:
    """
    print(request.POST)
    # <QueryDict: {'csrfmiddlewaretoken': ['hBlBWfxGFhDXaqDCfkSMFhKd6ZhZsbuqM8TEj3upzwe2NynenybodHgQyFHQAvZ0'],
    #               'is_up': ['true'], 'article_id': ['1']}>

    article_id = request.POST.get("article_id")
    # is_up = request.POST.get("is_up")    # 拿到的是一個字符串 "true"
    is_up = json.loads(request.POST.get("is_up"))   # 反序列化,拿到一個bool值

    # 點贊人即當前登陸人
    user_id = request.user.pk

    # 用戶只要在點贊表存有記錄就不能再存了
    obj = models.ArticleUpDown.objects.filter(user_id=user_id, article_id=article_id).first()

    response = {"state": True}
    if not obj:
        # 建立一條新記錄
        ard = models.ArticleUpDown.objects.create(user_id=user_id, article_id=article_id, is_up=is_up)

        queryset = models.Article.objects.filter(pk=article_id)
        if is_up:
            queryset.update(up_count=F("up_count")+1)
        else:
            queryset.update(down_count=F("down_count")+1)
    else:
        # 重複點贊提示,告訴ajax已經推薦過了
        response["state"] = False
        response["handled"] = obj.is_up   # True:推薦過了, Flase: 踩過了

    return JsonResponse(response)   # 返回response字典

  注意:

(1)經過models.ArticleUpDown.objects.filter(user_id=user_id, article_id=article_id).first(),拿到當前用戶在點贊表中針對該文章的點贊記錄。if not obj則能夠斷定沒有點贊過,能夠正常操做。

(2)提早建立response字典,將須要傳遞給ajax的信息放入其中。而後引入JsonResponse傳遞字典。

二、在ajax中處理重複錯誤信息

<script>
    $('#div_digg .action').click(function () {
        var is_up = $(this).hasClass("diggit");
        $.ajax({
            url: "/digg/",
            type: "post",
            data: {
                'csrfmiddlewaretoken': $("[name= 'csrfmiddlewaretoken']").val(),
                "is_up": is_up,
                "article_id": "{{ article_obj.pk }}"
            },
            success: function (data) {
                console.log(data);
                if (data.state) {

                } else {
                    if (data.handled) {
                        $("#digg_tips").html("您已經推薦過!")
                    } else {
                        $("#digg_tips").html("您已經反對過!")
                    }
                    setTimeout(function () {
                        $("#digg_tips").html("")
                    }, 1000)
                }
            },
        })
    })
</script>

  注意:

(1)在data.state爲false時,判斷data.handled字典是True/False,在id="digg_tips"標籤添加對應的內容。

(2)運用setTimeout函數,在提示消息顯示1秒後,自動隱藏。

(3)顯示效果以下:

  

6、點贊數ajax更新

  修改article_detail.html內script代碼:

<script>
    $('#div_digg .action').click(function () {
        var is_up = $(this).hasClass("diggit");
        $.ajax({
            url: "/digg/",
            type: "post",
            data: {
                'csrfmiddlewaretoken': $("[name= 'csrfmiddlewaretoken']").val(),
                "is_up": is_up,
                "article_id": "{{ article_obj.pk }}"
            },
            success: function (data) {
                console.log(data);
                if (data.state) {
                    if (is_up){
                        var val = parseInt($("#digg_count").text());  // parseInt() 函數可解析一個字符串,並返回一個整數。
                        $("#digg_count").text(val+1);
                    } else {
                        var val = parseInt($("#bury_count").text());
                        $("#bury_count").text(val+1);
                    }

                } else {
                    if (data.handled) {
                        $("#digg_tips").html("您已經推薦過!")
                    } else {
                        $("#digg_tips").html("您已經反對過!")
                    }
                    setTimeout(function () {
                        $("#digg_tips").html("")
                    }, 1000)
                }
            },
        })
    })
</script>

  這樣在點擊推薦或踩滅後,不用刷新頁面,第一時間就顯示了數字加1。

7、代碼優化

  主要針對article_detail.html中js的重複代碼。

{% extends "base.html" %}

{% block content %}
    {%  csrf_token %}
    <h3 class="text-center">{{ article_obj.title }}</h3>
    <div class="cont">
        {{ article_obj.content|safe }}
    </div>

    {# 文章點贊 #}
    <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>

<script>
    $('#div_digg .action').click(function () {
        var is_up = $(this).hasClass("diggit");

        $obj = $(this).children("span");

        $.ajax({
            url: "/digg/",
            type: "post",
            data: {
                'csrfmiddlewaretoken': $("[name= 'csrfmiddlewaretoken']").val(),
                "is_up": is_up,
                "article_id": "{{ article_obj.pk }}"
            },
            success: function (data) {
                console.log(data);
                if (data.state) {
                    var val = parseInt($obj.text());  // parseInt() 函數可解析一個字符串,並返回一個整數。
                    $obj.text(val+1);
                } else {
                    // 三元表達式
                    var val = data.handled?"您已經推薦過!":"您已經反對過!";
                    $("#digg_tips").html(val);

                    setTimeout(function () {
                        $("#digg_tips").html("")
                    }, 1000)
                }
            },
        })
    })
</script>
{% endblock %}

  注意:

(1)$obj = $(this).children("span");  變量定義,取消了is_up的判斷。

(2)應用三元表達式true對應已經推薦過,false對應已經反對過

// 三元表達式
var val = data.handled?"您已經推薦過!":"您已經反對過!";
相關文章
相關標籤/搜索