將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
給推薦和反對的這兩個標籤綁定事件,一點擊就發送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 %}
<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表記錄,確認新記錄是否生成:
生成一條贊記錄,就應該把贊記錄對應的文章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")
工做原理是,直接在數據庫中查出數據,計數後更改數據庫。點贊後刷新頁面,點贊次數已經更新:
博客園文章點贊規則:用戶對一篇文章點贊後,不容許再對文章進行點贊或踩滅操做。
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傳遞字典。
<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)顯示效果以下:
修改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?"您已經推薦過!":"您已經反對過!";