1. 程式人生 > >Django搭建部落格網站(2)

Django搭建部落格網站(2)

10、頁面側邊欄:使用自定義模板標籤

我們的部落格側邊欄有四項內容:最新文章、歸檔、分類和標籤雲。這些內容相對比較固定,且在各個頁面都會顯示,如果像文章列表或者文章詳情一樣,從檢視函式中獲取然後傳遞給模板,則每個頁面對應的檢視函式裡都要寫一段獲取這些內容的程式碼,這會導致很多重複程式碼。更好的解決方案是直接在模板中獲取,為此,我們使用 Django 的一個新技術:自定義模板標籤來完成任務。

使用模板標籤的思路

我們前面已經接觸過一些 Django 內建的模板標籤,比如比較簡單的 {% static %} 模板標籤,這個標籤幫助我們在模板中引入靜態檔案。還有比較複雜的如 {% for %} {% endfor%} 標籤。這裡 我們希望自己定義一個模板標籤,例如名為 get_recent_posts

 的模板標籤,它可以這樣工作:我們只要在模板中寫入 {% get_recent_posts as recent_post_list %},那麼模板中就會有一個從資料庫獲取的最新文章列表,並通過 as 語句儲存到 recent_post_list 模板變數裡。這樣我們就可以通過 {% for %} {% endfor%} 模板標籤來迴圈這個變數,顯示最新文章列表了,這和我們在編寫部落格首頁面檢視函式是類似的。首頁檢視函式中從資料庫獲取文章列表並儲存到 post_list 變數,然後把這個 post_list 變數傳給模板,模板使用 for 模板標籤迴圈這個文章列表變數,從而展示一篇篇文章。這裡唯一的不同是我們從資料庫獲取文章列表的操作不是在檢視函式中進行,而是在模板中通過自定義的 {% get_recent_posts %} 模板標籤進行。

以上就是解決思路,但模板標籤不是我們隨意寫的,必須遵循 Django 的規範我們才能在 Django 的模板系統中使用自定義的模板標籤,下面我們就依照這些規範來實現我們的需求。

模板標籤目錄結構

首先在我們的 blog 應用下建立一個 templatetags 資料夾。然後在這個資料夾下建立一個 __init__.py 檔案,使這個資料夾成為一個 Python 包,之後在 templatetags\ 目錄下建立一個 blog_tags.py 檔案,這個檔案存放自定義的模板標籤程式碼。

接下來就是編寫各個模板標籤的程式碼了,自定義模板標籤程式碼寫在 blog_tags.py 檔案中。其實模板標籤本質上就是一個 Python 函式,因此按照 Python 函式的思路來編寫模板標籤的程式碼就可以了

10.1.最新文章模板標籤

開啟 blog_tags.py 檔案,開始寫最新文章模板標籤。

blog/templatetags/blog_tags.py

from ..models import Post

def get_recent_posts(num=5):
return Post.objects.all().order_by(’-created_time’)[:num]

這個函式的功能是獲取資料庫中前 num 篇文章,這裡 num 預設為 5。函式就這麼簡單,但目前它還只是一個純 Python 函式,Django 在模板中還不知道該如何使用它。為了能夠通過 {% get_recent_posts %} 的語法在模板中呼叫這個函式,必須按照 Django 的規定註冊這個函式為模板標籤,方法如下:

blog/templatetags/blog_tags.py

from django import template
from ..models import Post

register = template.Library()

@register.simple_tag
def get_recent_posts(num=5):
return Post.objects.all().order_by(’-created_time’)[:num]

這裡首先匯入 template 這個模組,然後例項化了一個 template.Library 類,並將函式 get_recent_posts 裝飾為 register.simple_tag。這樣就可以在模板中使用語法 {% get_recent_posts %} 呼叫這個函數了。

10.2.歸檔模板標籤

和最新文章模板標籤一樣,先寫好函式,然後將函式註冊為模板標籤即可。

blog/templatetags/blog_tags.py

@register.simple_tag
def archives():
return Post.objects.dates(‘created_time’, ‘month’, order=‘DESC’)

這裡 dates 方法會返回一個列表,列表中的元素為每一篇文章(Post)的建立時間,且是 Python 的 date 物件,精確到月份,降序排列。接受的三個引數值表明了這些含義,一個是 created_time ,即 Post 的建立時間,month 是精度,order=‘DESC’ 表明降序排列(即離當前越近的時間越排在前面)。例如我們寫了 3 篇文章,分別釋出於 2018 年 2 月 14 日、2018 年 3 月 14 日、2018 年 3 月 15 日,那麼 dates 函式將返回 2018 年 3 月 和 2018 年 2 月這樣一個時間列表,且降序排列,從而幫助我們實現按月歸檔的目的。

10.3.分類標籤模板

過程還是一樣,先寫好函式,然後將函式註冊為模板標籤。先匯入Categor類

blog/templatetags/blog_tags.py

from ..models import Post, Category

@register.simple_tag
def get_categories():
# 別忘了在頂部引入 Category 類
return Category.objects.all()

儘管側邊欄有 4 項內容(還有一個標籤雲),但是這裡我們只實現最新文章、歸檔和分類資料的顯示,還有一個標籤雲沒有實現。因為標籤雲的實現稍有一點不同

10.4.使用自定的模板標籤

開啟 base.html,為了使用模板標籤,我們首先需要在模板中匯入存放這些模板標籤的模組,這裡是 blog_tags.py 模組。當時我們為了使用 static 模板標籤時曾經匯入過 {% load staticfiles %},這次在 {% load staticfiles %} 下再匯入 blog_tags:

templates/base.html

{% load staticfiles %}
{% load blog_tags %}
<!DOCTYPE html>
<html>

</html>

然後找到最新文章列表處,把裡面的列表修改一下:

templates/base.html

<div class=“widget widget-recent-posts”>
<h3 class=“widget-title”>最新文章</h3>
{% get_recent_posts as recent_post_list %}
<ul>
{% for post in recent_post_list %}
<li>
<a href="{{ post.get_absolute_url }}">{{ post.title }}</a>
</li>
{% empty %}
暫無文章!
{% endfor %}
</ul>
</div>

這裡我們通過使用 get_recent_posts 模板標籤獲取到最新文章列表,然後我們通過 as 語法(Django 模板系統的語法)將獲取的文章列表儲存進了 recent_post_list 模板變數中,之後就可以通過 for 迴圈來迴圈顯示文章列表資料了,這和我們在寫首頁檢視時是一樣的。

然後是歸檔部分:

templates/base.html

<div class=“widget widget-archives”>
<h3 class=“widget-title”>歸檔</h3>
{% archives as date_list %}
<ul>
{% for date in date_list %}
<li>
<a href="#">{{ date.year }}{{ date.month }}</a>
</li>
{% empty %}
暫無歸檔!
{% endfor %}
</ul>
</div>

同樣,這裡我們呼叫 archives 模板標籤自動獲取一個已發表文章的日期列表,精確到月份,降序排列,然後通過 as 語法將其儲存在 date_list 模板變數裡。由於日期列表中的元素為 Python 的 date 物件,因此可以通過其 year 和 month 屬性分別獲取年和月的資訊,<a href="#">{{ date.year }} 年 {{ date.month }} 月</a> 反應了這個事實。

分類部分也一樣:

<div class=“widget widget-category”>
<h3 class=“widget-title”>分類</h3>
{% get_categories as category_list %}
<ul>
{% for category in category_list %}
<li>
<a href="#">{{ category.name }} <span class=“post-count”>(13)</span></a>
</li>
{% empty %}
暫無分類!
{% endfor %}
</ul>
</div>

<span class=“post-count”>(13)</span> 顯示的是該分類下的文章數目,這個特性會在接下來的教程中講解如何實現,目前暫時用佔位資料代替吧。

現在執行開發伺服器,可以看到側邊欄顯示的資料已經不再是之前的佔位資料,而是我們儲存在資料庫中的資料了。

 十一、分類與歸檔

 側邊欄已經正確地顯示了最新文章列表、歸檔、分類等資訊。現在來完善歸檔和分類功能,當用戶點選歸檔下的某個日期或者分類下的某個分類時,跳轉到文章列表頁面,顯示該日期或者分類下的全部文章。

 11.1.歸檔頁面

要顯示某個歸檔日期下的文章列表,思路和顯示主頁文章列表是一樣的,回顧一下主頁檢視的程式碼:

blog/views.py

def index(request):
post_list = Post.objects.all().order_by(’-created_time’)
return render(request, ‘blog/index.html’, {‘post_list’: post_list})

主頁檢視函式中我們通過 Post.objects.all() 獲取全部文章,而在我們的歸檔和分類檢視中,我們不再使用 all 方法獲取全部文章,而是使用 filter 來根據條件過濾。先來看歸檔檢視:

blog/views.py

def archives(request, year, month):
post_list = Post.objects.filter(created_time__year=year,
created_time__month=month
).order_by(’-created_time’)
return render(request, ‘blog/index.html’, {‘post_list’: post_list})

這裡我們使用了模型管理器(objects)的 filter 函式來過濾文章。由於是按照日期歸檔,因此這裡根據文章發表的年和月來過濾。具體來說,就是根據 created_time 的 year 和 month 屬性過濾,篩選出文章發表在對應的 year 年和 month 月的文章。注意這裡 created_time 是 Python 的 date 物件,其有一個 year 和 month 屬性,我們在 頁面側邊欄:使用自定義模板標籤 使用過這個屬性。Python 中類例項呼叫屬性的方法通常是 created_time.year,但是由於這裡作為函式的引數列表,所以 Django 要求我們把點替換成了兩個下劃線,即 created_time__year。同時和 index 檢視中一樣,我們對返回的文章列表進行了排序。此外由於歸檔的下的文章列表的顯示和首頁是一樣的,因此我們直接渲染了index.html 模板。

 寫好檢視函式後就是配置好 URL:

blog/urls.py

from django.conf.urls import url

from . import views

app_name = ‘blog’
urlpatterns = [
url(r’^KaTeX parse error: Expected 'EOF', got '&' at position 425: …ing">'^post/(?P&̲lt;pk&gt;[0-9]+…, views.detail, name=‘detail’),
url(r’^archives/(?P<year>[0-9]{4})/(?P<month>[0-9]{1,2})/$’, views.archives, name=‘archives’),
]

這個歸檔檢視對應的 URL 的正則表示式和 detail 檢視函式對應的 URL 是類似的,這在之前我們講過。兩個括號括起來的地方是兩個命名組引數,Django 會從使用者訪問的 URL 中自動提取這兩個引數的值,然後傳遞給其對應的檢視函式。例如如果使用者想檢視 2018 年 3 月下的全部文章,他訪問 /archives/2018/3/,那麼 archives 檢視函式的實際呼叫為:archives(request, year=2018, month=3)

在模板找到歸檔列表部分的程式碼,修改超連結的 href 屬性,讓使用者點選超連結後跳轉到文章歸檔頁面:

templates/base.html

{% for date in date_list %}
<li>
<a href="{% url ‘blog:archives’ date.year date.month %}">
{{ date.year }}{{ date.month }}
</a>
</li>
{% endfor %}

這裡 {% url %} 這個模板標籤的作用是解析檢視函式 blog:archives 對應的 URL 模式,並把 URL 模式中的年和月替換成 date.yeardate.month 的值。例如 blog:archives 表示 blog 應用下的 archives 函式,這個函式對應的 URL 模式為 ^archives/(?P<year>[0-9]{4})/(?P<month>[0-9]{1,2})/$,假設 date.year=2018date.month=5,那麼 {% url ‘blog:archives’ date.year date.month %} 模板標籤返回的值為/archives/2018/5/。

為什麼要使用 {% url %} 模板標籤呢?事實上,我們把超連結的 href 屬性設定為 /archives/{{ date.year }}/{{ date.month }}/ 同樣可以達到目的,但是這種寫法是硬編碼的。雖然現在 blog:archives 檢視函式對應的 URL 模式是這種形式,但是如果哪天這個模式改變了呢?如果使用了硬編碼的寫法,那你需要把每一處 /archives/{{ date.year }}/{{ date.month }}/ 修改為新的模式。但如果使用了 {% url %} 模板標籤,則不用做任何修改。

 測試一下,點選側邊欄歸檔的日期,跳轉到歸檔頁面,發現並沒有顯示歸檔下的文章列表,因為還要改一下時區:

首先安裝pytz模組(django處理時區用的,安裝即可,無需其它操作),然後更改settings設定

#settings.py

LANGUAGE_CODE = ‘zh-hans’

TIME_ZONE = ‘Asia/Shanghai’

USE_I18N = True

USE_L10N = True

USE_TZ = False

再次測試,發現可以顯示歸檔下的文章列表了。

 11.2.分類頁面

同樣的寫好分類頁面的檢視函式:

blog/views.py

import markdown

from django.shortcuts import render, get_object_or_404

引入 Category 類

from .models import Post, Category

def category(request, pk):
# 記得在開始部分匯入 Category 類
cate = get_object_or_404(Category, pk=pk)
post_list = Post.objects.filter(category=cate).order_by(’-created_time’)
return render(request, ‘blog/index.html’, context={‘post_list’: post_list})

這裡我們首先根據傳入的 pk 值(也就是被訪問的分類的 id 值)從資料庫中獲取到這個分類。get_object_or_404 函式和 detail 檢視中一樣,其作用是如果使用者訪問的分類不存在,則返回一個 404 錯誤頁面以提示使用者訪問的資源不存在。然後我們通過 filter 函式過濾出了該分類下的全部文章。同樣也和首頁檢視中一樣對返回的文章列表進行了排序。

URL 配置如下:

blog/urls.py

from django.conf.urls import url

from . import views

app_name = ‘blog’
urlpatterns = [
url(r’^KaTeX parse error: Expected 'EOF', got '&' at position 425: …ing">'^post/(?P&̲lt;pk&gt;[0-9]+…, views.detail, name=‘detail’),
url(r’^archives/(?P<year>[0-9]{4})/(?P<month>[0-9]{1,2})/KaTeX parse error: Expected 'EOF', got '&' at position 435: …>'^category/(?P&̲lt;pk&gt;[0-9]+…, views.category, name=‘category’),
]

修改相應模板:

templates/base.html

{% for category in category_list %}
<li>
<a href="{% url ‘blog:category’ category.pk %}">{{ category.name }}</a>
</li>
{% endfor %}

同樣,{% url %} 模板標籤的用法和寫歸檔頁面時的用法是一樣的。現在嘗試點選相應的連結,就可以跳轉到歸檔或者分類頁面了。

 十二、評論

 相對來說,評論其實是另外一個比較獨立的功能。Django 提倡,如果功能相對比較獨立的話,最好是建立一個應用,把相應的功能程式碼寫到這個應用裡。我們的第一個應用叫 blog,它裡面放了展示部落格文章列表和細節等相關功能的程式碼。而這裡我們再建立一個應用,名為 comments,這裡面將存放和評論功能相關的程式碼。

python manage.py startapp comments

建立新的應用後一定要記得在 settings.py 裡註冊這個應用,Django 才知道這是一個應用

INSTALLED_APPS = [
‘django.contrib.admin’,
‘django.contrib.auth’,
‘django.contrib.contenttypes’,
‘django.contrib.sessions’,
‘django.contrib.messages’,
‘django.contrib.staticfiles’,
‘blog’,
‘comments’
]

12.1.設計評論的資料庫模型

 使用者評論的資料必須被儲存到資料庫裡,以便其他使用者訪問時 Django 能從資料庫取回這些資料然後展示給訪問的使用者,因此我們需要為評論設計資料庫模型,這和設計文章、分類、標籤的資料庫模型是一樣的,評論模型設計如下(評論模型的程式碼寫在 comment\models.py 裡):

comments/models.py

from django.db import models
class Comment(models.Model):
name = models.CharField(max_length=100)
email = models.EmailField(max_length=255)
url = models.URLField(blank=True)
text = models.TextField()
created_time = models.DateTimeField(auto_now_add=True)

post <span class="token operator">=</span> models<span class="token punctuation">.</span><span class="token function">ForeignKey</span><span class="token punctuation">(</span><span class="token string">'blog.Post'</span><span class="token punctuation">)</span>

def <span class="token function">__str__</span><span class="token punctuation">(</span>self<span class="token punctuation">)</span><span class="token punctuation">:</span>
    <span class="token keyword">return</span> self<span class="token punctuation">.</span>text<span class="token punctuation">[</span><span class="token punctuation">:</span><span class="token number">20</span><span class="token punctuation">]</span></pre><p>這裡我們會儲存評論使用者的 name(名字)、email(郵箱)、url(個人網站),使用者發表的內容將存放在 text 欄位裡,created_time 記錄評論時間。最後,這個評論是關聯到某篇文章(Post)的,由於一個評論只能屬於一篇文章,一篇文章可以有多個評論,是一對多的關係,因此這裡我們使用了 ForeignKey。關於 ForeKey 我們前面已有介紹,這裡不再贅述。</p><p>同時注意我們為&nbsp;<code>DateTimeField</code>&nbsp;傳遞了一個&nbsp;<code>auto_now_add=True</code>&nbsp;的引數值。<code>auto_now_add</code>&nbsp;的作用是,當評論資料儲存到資料庫時,自動把&nbsp;<code>created_time</code>&nbsp;的值指定為當前時間。<code>created_time</code>&nbsp;記錄使用者發表評論的時間,我們肯定不希望使用者在發表評論時還得自己手動填寫評論發表時間,這個時間應該自動生成。</p><p>建立了資料庫模型就要遷移資料庫,分別執行下面兩條命令:</p><pre class="prism-token token  language-javascript">python manage<span class="token punctuation">.</span>py makemigrations

python manage.py migrate

12.2.評論表單設計

下面開始編寫評論表單程式碼。在 comments\ 目錄下(和 models.py 同級)新建一個 forms.py 檔案,用來存放表單程式碼,我們的表單程式碼如下:

comments/forms.py

from django import forms
from .models import Comment

class CommentForm(forms.ModelForm):
class Meta:
model = Comment
fields = [‘name’, ‘email’, ‘url’, ‘text’]

要使用 Django 的表單功能,我們首先匯入 forms 模組。Django 的表單類必須繼承自 forms.Form 類或者 forms.ModelForm 類。如果表單對應有一個數據庫模型(例如這裡的評論表單對應著評論模型),那麼使用 ModelForm類會簡單很多,這是 Django 為我們提供的方便。之後我們在表單的內部類 Meta 裡指定一些和表單相關的東西。model = Comment 表明這個表單對應的資料庫模型是 Comment 類。fields = [‘name’, ‘email’, ‘url’, ‘text’] 指定了表單需要顯示的欄位,這裡我們指定了 name、email、url、text 需要顯示。

12.3.評論檢視函式

當用戶提交表單中的資料後,Django 需要呼叫相應的檢視函式來處理這些資料,下面開始寫我們檢視函式處理邏輯:

comments/views.py

from django.shortcuts import render, get_object_or_404, redirect
from blog.models import Post

from .models import Comment
from .forms import CommentForm

def post_comment(request, post_pk):
# 先獲取被評論的文章,因為後面需要把評論和被評論的文章關聯起來。
# 這裡我們使用了 Django 提供的一個快捷函式 get_object_or_404,
# 這個函式的作用是當獲取的文章(Post)存在時,則獲取;否則返回 404 頁面給使用者。
post = get_object_or_404(Post, pk=post_pk)

# HTTP 請求有 <span class="token keyword">get</span> 和 post 兩種,一般使用者通過表單提交資料都是通過 post 請求,
# 因此只有當用戶的請求為 post 時才需要處理表單資料。
<span class="token keyword">if</span> request<span class="token punctuation">.</span>method <span class="token operator">==</span> <span class="token string">'POST'</span><span class="token punctuation">:</span>
    # 使用者提交的資料存在 request<span class="token punctuation">.</span>POST 中,這是一個類字典物件。
    # 我們利用這些資料構造了 CommentForm 的例項,這樣 Django 的表單就生成了。
    form <span class="token operator">=</span> <span class="token function">CommentForm</span><span class="token punctuation">(</span>request<span class="token punctuation">.</span>POST<span class="token punctuation">)</span>

    # 當呼叫 form<span class="token punctuation">.</span><span class="token function">is_valid</span><span class="token punctuation">(</span><span class="token punctuation">)</span> 方法時,Django 自動幫我們檢查表單的資料是否符合格式要求。
    <span class="token keyword">if</span> form<span class="token punctuation">.</span><span class="token function">is_valid</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">:</span>
        # 檢查到資料是合法的,呼叫表單的 save 方法儲存資料到資料庫,
        # commit<span class="token operator">=</span>False 的作用是僅僅利用表單的資料生成 Comment 模型類的例項,但還不儲存評論資料到資料庫。
        comment <span class="token operator">=</span> form<span class="token punctuation">.</span><span class="token function">save</span><span class="token punctuation">(</span>commit<span class="token operator">=</span>False<span class="token punctuation">)</span>

        # 將評論和被評論的文章關聯起來。
        comment<span class="token punctuation">.</span>post <span class="token operator">=</span> post

        # 最終將評論資料儲存進資料庫,呼叫模型例項的 save 方法
        comment<span class="token punctuation">.</span><span class="token function">save</span><span class="token punctuation">(</span><span class="token punctuation">)</span>

        # 重定向到 post 的詳情頁,實際上當 redirect 函式接收一個模型的例項時,它會呼叫這個模型例項的 get_absolute_url 方法,
        # 然後重定向到 get_absolute_url 方法返回的 URL。
        <span class="token keyword">return</span> <span class="token function">redirect</span><span class="token punctuation">(</span>post<span class="token punctuation">)</span>

    <span class="token keyword">else</span><span class="token punctuation">:</span>
        # 檢查到資料不合法,重新渲染詳情頁,並且渲染表單的錯誤。
        # 因此我們傳了三個模板變數給 detail<span class="token punctuation">.</span>html,
        # 一個是文章(Post),一個是評論列表,一個是表單 form
        # 注意這裡我們用到了 post<span class="token punctuation">.</span>comment_set<span class="token punctuation">.</span><span class="token function">all</span><span class="token punctuation">(</span><span class="token punctuation">)</span> 方法,
        # 這個用法有點類似於 Post<span class="token punctuation">.</span>objects<span class="token punctuation">.</span><span class="token function">all</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
        # 其作用是獲取這篇 post 下的的全部評論,
        # 因為 Post 和 Comment 是 ForeignKey 關聯的,
        # 因此使用 post<span class="token punctuation">.</span>comment_set<span class="token punctuation">.</span><span class="token function">all</span><span class="token punctuation">(</span><span class="token punctuation">)</span> 反向查詢全部評論。
        # 具體請看下面的講解。
        comment_list <span class="token operator">=</span> post<span class="token punctuation">.</span>comment_set<span class="token punctuation">.</span><span class="token function">all</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
        context <span class="token operator">=</span> <span class="token punctuation">{</span><span class="token string">'post'</span><span class="token punctuation">:</span> post<span class="token punctuation">,</span>
                   <span class="token string">'form'</span><span class="token punctuation">:</span> form<span class="token punctuation">,</span>
                   <span class="token string">'comment_list'</span><span class="token punctuation">:</span> comment_list
                   <span class="token punctuation">}</span>
        <span class="token keyword">return</span> <span class="token function">render</span><span class="token punctuation">(</span>request<span class="token punctuation">,</span> <span class="token string">'blog/detail.html'</span><span class="token punctuation">,</span> context<span class="token operator">=</span>context<span class="token punctuation">)</span>
# 不是 post 請求,說明使用者沒有提交資料,重定向到文章詳情頁。
<span class="token keyword">return</span> <span class="token function">redirect</span><span class="token punctuation">(</span>post<span class="token punctuation">)</span></pre><p>這個評論檢視相比之前的一些檢視複雜了很多,主要是處理評論的過程更加複雜。具體過程在程式碼中已有詳細註釋,這裡僅就檢視中出現了一些新的知識點進行講解。</p><p>首先我們使用了&nbsp;<code>redirect</code>&nbsp;函式。這個函式位於 django.shortcuts 模組中,它的作用是對 HTTP 請求進行重定向(即使用者訪問的是某個 URL,但由於某些原因,伺服器會將使用者重定向到另外的 URL)。<code>redirect</code>&nbsp;既可以接收一個 URL 作為引數,也可以接收一個模型的例項作為引數(例如這裡的 post)。如果接收一個模型的例項,那麼這個例項必須實現了&nbsp;<code>get_absolute_url</code>&nbsp;方法,這樣&nbsp;<code>redirect</code>&nbsp;會根據&nbsp;<code>get_absolute_url</code>&nbsp;方法返回的 URL 值進行重定向。</p><p>另外我們使用了&nbsp;<code>post.comment_set.all()</code>&nbsp;來獲取&nbsp;<code>post</code>&nbsp;對應的全部評論。&nbsp;<code>Comment</code>&nbsp;和<code>Post</code>&nbsp;是通過&nbsp;<code>ForeignKey</code>&nbsp;關聯的,回顧一下我們當初獲取某個分類&nbsp;<code>cate</code>&nbsp;下的全部文章時的程式碼:<code>Post.objects.filter(category=cate)</code>。這裡&nbsp;<code>post.comment_set.all()</code>&nbsp;也等價於&nbsp;<code>Comment.objects.filter(post=post)</code>,即根據&nbsp;<code>post</code>&nbsp;來過濾該&nbsp;<code>post</code>&nbsp;下的全部評論。但既然我們已經有了一個&nbsp;<code>Post</code>&nbsp;模型的例項&nbsp;<code>post</code>(它對應的是&nbsp;<code>Post</code>&nbsp;在資料庫中的一條記錄),那麼獲取和&nbsp;<code>post</code>&nbsp;關聯的評論列表有一個簡單方法,即呼叫它的 xxx_set 屬性來獲取一個類似於 objects 的模型管理器,然後呼叫其&nbsp;<code>all</code>&nbsp;方法來返回這個&nbsp;<code>post</code>&nbsp;關聯的全部評論。 其中 xxx_set 中的 xxx 為關聯模型的類名(小寫)。例如&nbsp;<code>Post.objects.filter(category=cate)</code>&nbsp;也可以等價寫為&nbsp;<code>cate.post_set.all()</code>。</p><h3>12.4.繫結url</h3><p>檢視函式需要和 URL 繫結,這裡我們在 comment 應用中再建一個 urls.py 檔案,寫上 URL 模式:</p><pre class="prism-token token  language-javascript">comments<span class="token operator">/</span>urls<span class="token punctuation">.</span>py

from django.conf.urls import url

from . import views

app_name = ‘comments’
urlpatterns = [
url(r’^comment/post/(?P<post_pk>[0-9]+)/$’, views.post_comment, name=‘post_comment’),
]

別忘了給這個評論的 URL 模式規定名稱空間,即 app_name = ‘comments’

最後要在專案的 blogprokect\ 目錄的 urls.py 裡包含 comments\urls.py 這個檔案:

blogproject/urls.py

urlpatterns = [
url(r’^admin/’, admin.site.urls),
url(r’’, include(‘blog.urls’)),
url(r’’, include(‘comments.urls’)),
]

12.5…更新文章詳情頁面的檢視函式

我們可以看到評論表單和評論列表是位於文章詳情頁面的,處理文章詳情頁面的檢視函式是 detail,相應地需要更新 detail,讓它生成表單和從資料庫獲取文章對應的評論列表資料,然後傳遞給模板顯示:

blog/views.py

import markdown

from django.shortcuts import render, get_object_or_404

+ from comments.forms import CommentForm
from .models import Post, Category

def detail(request, pk):
post = get_object_or_404(Post, pk=pk)
post.body = markdown.markdown(post.body,
extensions=[
‘markdown.extensions.extra’,
‘markdown.extensions.codehilite’,
‘markdown.extensions.toc’,
])
# 記得在頂部匯入 CommentForm
form = CommentForm()
# 獲取這篇 post 下的全部評論
comment_list = post.comment_set.all()

# 將文章、表單、以及文章下的評論列表作為模板變數傳給 detail<span class="token punctuation">.</span>html 模板,以便渲染相應資料。
context <span class="token operator">=</span> <span class="token punctuation">{</span><span class="token string">'post'</span><span class="token punctuation">:</span> post<span class="token punctuation">,</span>
           <span class="token string">'form'</span><span class="token punctuation">:</span> form<span class="token punctuation">,</span>
           <span class="token string">'comment_list'</s