我們的部落格側邊欄有四項内容:最新文章、歸檔、分類和标簽雲。這些内容相對比較固定,且在各個頁面都會顯示,如果像文章清單或者文章詳情一樣,從視圖函數中擷取然後傳遞給模闆,則每個頁面對應的視圖函數裡都要寫一段擷取這些内容的代碼,這會導緻很多重複代碼。更好的解決方案是直接在模闆中擷取,為此,我們使用 django 的一個新技術:自定義模闆标簽來完成任務。
我們前面已經接觸過一些 django 内置的模闆标簽,比如比較簡單的 {% static %} 模闆标簽,這個标簽幫助我們在模闆中引入靜态檔案。還有比較複雜的如 {% for %} {% endfor%} 标簽。這裡 我們希望自己定義一個模闆标簽,例如名為 <code>get_recent_posts</code> 的模闆标簽,它可以這樣工作:我們隻要在模闆中寫入 {% get_recent_posts as recent_post_list %},那麼模闆中就會有一個從資料庫擷取的最新文章清單,并通過 as 語句儲存到 <code>recent_post_list</code> 模闆變量裡。這樣我們就可以通過 {% for %} {% endfor%} 模闆标簽來循環這個變量,顯示最新文章清單了,這和我們在編寫部落格首頁面視圖函數是類似的。首頁視圖函數中從資料庫擷取文章清單并儲存到 <code>post_list</code> 變量,然後把這個 <code>post_list</code> 變量傳給模闆,模闆使用 for 模闆标簽循環這個文章清單變量,進而展示一篇篇文章。這裡唯一的不同是我們從資料庫擷取文章清單的操作不是在視圖函數中進行,而是在模闆中通過自定義的 {% get_recent_posts %} 模闆标簽進行。
以上就是解決思路,但模闆标簽不是我們随意寫的,必須遵循 django 的規範我們才能在 django 的模闆系統中使用自定義的模闆标簽,下面我們就依照這些規範來實作我們的需求。
首先在我們的 blog 應用下建立一個 templatetags 檔案夾。然後在這個檔案夾下建立一個 __init__.py 檔案,使這個檔案夾成為一個 python 包,之後在 templatetags 目錄下建立一個 blog_tags.py 檔案,這個檔案存放自定義的模闆标簽代碼。
此時你的目錄結構應該是這樣的:
接下來就是編寫各個模闆标簽的代碼了,自定義模闆标簽代碼寫在 blog_tags.py 檔案中。其實模闆标簽本質上就是一個 python 函數,是以按照 python 函數的思路來編寫模闆标簽的代碼就可以了,并沒有任何新奇的東西或者需要新學習的知識在裡面。
打開 blog_tags.py 檔案,開始寫我們的最新文章模闆标簽。
這個函數的功能是擷取資料庫中前 <code>num</code> 篇文章,這裡 <code>num</code> 預設為 5。函數就這麼簡單,但目前它還隻是一個純 python 函數,django 在模闆中還不知道該如何使用它。為了能夠通過 {% get_recent_posts %} 的文法在模闆中調用這個函數,必須按照 django 的規定注冊這個函數為模闆标簽,方法如下:
這裡我們首先導入 template 這個子產品,然後執行個體化了一個 <code>template.library</code> 類,并将函數 <code>get_recent_posts</code> 裝飾為 <code>register.simple_tag</code>。這樣就可以在模闆中使用文法 {% get_recent_posts %} 調用這個函數了。
注意 django 1.9 後才支援 simple_tag 模闆标簽,如果你使用的 django 版本小于 1.9,你将得到一個錯誤。django 1.9 以前的版本如何自定義模闆标簽這裡不再贅述。
和最新文章模闆标簽一樣,先寫好函數,然後将函數注冊為模闆标簽即可。
這裡 <code>dates</code> 方法會傳回一個清單,清單中的元素為每一篇文章(post)的建立時間,且是 python 的 <code>date</code> 對象,精确到月份,降序排列。接受的三個參數值表明了這些含義,一個是 <code>created_time</code> ,即 <code>post</code> 的建立時間,<code>month</code> 是精度,<code>order='desc'</code> 表明降序排列(即離目前越近的時間越排在前面)。例如我們寫了 3 篇文章,分别釋出于 2017 年 2 月 21 日、2017 年 3 月 25 日、2017 年 3 月 28 日,那麼 <code>dates</code> 函數将傳回 2017 年 3 月 和 2017 年 2 月這樣一個時間清單,且降序排列,進而幫助我們實作按月歸檔的目的。
過程還是一樣,先寫好函數,然後将函數注冊為模闆标簽。注意分類模闆标簽函數中使用到了 <code>category</code> 類,其定義在 blog.models.py 檔案中,使用前記得先導入它,否則會報錯。
盡管側邊欄有 4 項内容(還有一個标簽雲),但是這裡我們隻實作最新文章、歸檔和分類資料的顯示,還有一個标簽雲沒有實作。因為标簽雲的實作稍有一點不同,是以将在接下來的教程中專門介紹。這裡你也可以嘗試着自己解決,如果遇到問題,可以通過官方文檔或者搜尋引擎求助。獨立思考并尋求解決方案以及善用搜尋引擎是一個開發者必須培養的能力,隻有這樣你才能成為一個獨立的開發者,獨立地解決别人可能從來沒有遇到過的問題。
打開 base.html,為了使用模闆标簽,我們首先需要在模闆中導入存放這些模闆标簽的子產品,這裡是 blog_tags.py 子產品。當時我們為了使用 static 模闆标簽時曾經導入過 {% load staticfiles %},這次在 {% load staticfiles %} 下再導入 blog_tags:
然後找到最新文章清單處,把裡面的清單修改一下:
這裡我們通過使用 <code>get_recent_posts</code> 模闆标簽擷取到最新文章清單,然後我們通過 as 文法(django 模闆系統的文法)将擷取的文章清單儲存進了 <code>recent_post_list</code> 模闆變量中,之後就可以通過 for 循環來循環顯示文章清單資料了,這和我們在寫首頁視圖時是一樣的。
然後是歸檔部分:
同樣,這裡我們調用 <code>archives</code> 模闆标簽自動擷取一個已發表文章的日期清單,精确到月份,降序排列,然後通過 as 文法将其儲存在 <code>date_list</code> 模闆變量裡。由于日期清單中的元素為 python 的 <code>date</code> 對象,是以可以通過其 <code>year</code> 和 <code>month</code> 屬性分别擷取年和月的資訊,<code><a href="#">{{ date.year }} 年 {{ date.month }} 月</a></code> 反應了這個事實。
分類部分也一樣:
<code><span class="post-count">(13)</span></code> 顯示的是該分類下的文章數目,這個特性會在接下來的教程中講解如何實作,目前暫時用占位資料代替吧。
現在運作開發伺服器,可以看到側邊欄顯示的資料已經不再是之前的占位資料,而是我們儲存在資料庫中的資料了。
注意:如果你按照教程的步驟做完後發現報錯,請按以下順序檢查。
檢查目錄結構是否正确。確定 templatetags 位于 blog 目錄下,且目錄名必須為 templatetags。具體請對照上文給出的目錄結構。
確定 templatetags 目錄下有 __init__.py 檔案。
確定使用的 django 版本不小于 1.9。
確定通過 <code>register = template.library()</code> 和 <code>@register.simple_tag</code> 裝飾器将函數裝飾為一個模闆标簽。
確定在使用模闆标簽以前導入了 blog_tags,即 {% load blog_tags %}。注意要在使用任何 blog_tags 下的模闆标簽以前導入它。
確定模闆标簽的文法使用正确,即 {% load blog_tags %},注意 { 和 % 以及 % 和 } 之間沒有任何空格。
如果遇到問題,請通過下面的方式尋求幫助。
将問題的較長的描述通過郵件發送到 [email protected],一般會在 24 小時内回複。