閱讀134 返回首頁    go 阿裏雲 go 技術社區[雲棲]


Django 博客開發教程 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\
    __init__.py
    admin.py
    apps.py
    migrations\
        __init__.py
    models.py
    static\
    templatetags\
        __init__.py
        blog_tags.py
    tests.py
    views.py

編寫模板標簽代碼

接下來就是編寫各個模板標簽的代碼了,自定義模板標簽代碼寫在 blog_tags.py 文件中。其實模板標簽本質上就是一個 Python 函數,因此按照 Python 函數的思路來編寫模板標簽的代碼就可以了,並沒有任何新奇的東西或者需要新學習的知識在裏麵。

最新文章模板標簽

打開 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 %} 調用這個函數了。

注意 Django 1.9 後才支持 simple_tag 模板標簽,如果你使用的 Django 版本小於 1.9,你將得到一個錯誤。Django 1.9 以前的版本如何自定義模板標簽這裏不再贅述。

歸檔模板標簽

和最新文章模板標簽一樣,先寫好函數,然後將函數注冊為模板標簽即可。

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 篇文章,分別發布於 2017 年 2 月 21 日、2017 年 3 月 25 日、2017 年 3 月 28 日,那麼 dates 函數將返回 2017 年 3 月 和 2017 年 2 月這樣一個時間列表,且降序排列,從而幫助我們實現按月歸檔的目的。

分類模板標簽

過程還是一樣,先寫好函數,然後將函數注冊為模板標簽。注意分類模板標簽函數中使用到了 Category 類,其定義在 blog.models.py 文件中,使用前記得先導入它,否則會報錯。

blog/templatetags/blog_tags.py

from ..models import Post, Category

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

盡管側邊欄有 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 >
  <h3 >最新文章</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 >
  <h3 >歸檔</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 對象,因此可以通過其 yearmonth 屬性分別獲取年和月的信息,<a href="#">{{ date.year }} 年 {{ date.month }} 月</a> 反應了這個事實。

分類部分也一樣:

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

<span >(13)</span> 顯示的是該分類下的文章數目,這個特性會在接下來的教程中講解如何實現,目前暫時用占位數據代替吧。

現在運行開發服務器,可以看到側邊欄顯示的數據已經不再是之前的占位數據,而是我們保存在數據庫中的數據了。

注意:如果你按照教程的步驟做完後發現報錯,請按以下順序檢查。

  1. 檢查目錄結構是否正確。確保 templatetags\ 位於 blog\ 目錄下,且目錄名必須為 templatetags。具體請對照上文給出的目錄結構。
  2. 確保 templatetags\ 目錄下有 __init__.py 文件。
  3. 確保使用的 Django 版本不小於 1.9。
  4. 確保通過 register = template.Library()@register.simple_tag 裝飾器將函數裝飾為一個模板標簽。
  5. 確保在使用模板標簽以前導入了 blog_tags,即 {% load blog_tags %}。**注意要在使用任何 blog_tags 下的模板標簽以前導入它。**
  6. 確保模板標簽的語法使用正確,即 {% load blog_tags %},注意 { 和 % 以及 % 和 } 之間沒有任何空格。

總結

本章節的代碼位於:Step10: side bar

如果遇到問題,請通過下麵的方式尋求幫助。

更多Django 教程,請訪問 追夢人物的博客Python 中文社區

最後更新:2017-06-01 21:02:16

  上一篇:go  Django 博客開發教程 11 - 分類與歸檔
  下一篇:go  6月1日雲棲精選夜讀:百花開放笑聲甜,“開源萌寵”慶六一