一、文章點贊樣式構建
1、將base.html的css樣式改為外部引入
將base.html的內嵌樣式刪除,改為使用 HTML 頭部的 <head> 標簽對中使用<link>標簽來引入外部的 CSS 文件。
base.html內容如下所示:
<!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:
* {
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:
.article_info .title {
margin-bottom: 20px;
}
上述css代碼是將標題部分和文字主體部分錯開20像素。
2、構建點贊樣式
根據博客園代碼,在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">
<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中:
.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)
給推薦和反對的這兩個標簽綁定事件,一點擊就發送ajax請求。
另外查看blog_articleupdown表:

其中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 %}
三、文章點贊保存
1、在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。
2、生成贊記錄
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表記錄,確認新記錄是否生成:

四、文章點贊數的數據同步
生成一條贊記錄,就應該把贊記錄對應的文章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")
工作原理是,直接在數據庫中查出數據,計數后更改數據庫。點贊后刷新頁面,點贊次數已經更新:

五、點贊提示重復操作
博客園文章點贊規則:用戶對一篇文章點贊后,不允許再對文章進行點贊或踩滅操作。
1、根據用戶在點贊是否有記錄進行判斷
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傳遞字典。
2、在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)顯示效果如下:

六、點贊數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。
七、代碼優化
主要針對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?"您已經推薦過!":"您已經反對過!";
