1. 程式人生 > >Django第四篇-----模板後半篇

Django第四篇-----模板後半篇

目錄

基本的標籤和過濾器

 Django Template Language(DTL)設計理念

在檢視中使用模板

模板載入機制

 模板目錄

render()

模板子目錄

 include 模板標籤

模板繼承

使用模板繼承的一些指導方針:


基本的標籤和過濾器

模板過濾器是在顯示變數之前調整變數值的簡單方式。過濾器使用管道符號指定,如下所示

{{ name|lower }}

上述程式碼先通過 lower 過濾器調整 {{ name }} 變數的值——把文字轉換成小寫,然後再顯示。過濾器可以串接,即把一個過濾器的輸出傳給下一個過濾器。

下述示例獲取列表中的第一個元素,然後將其轉換成大寫:

{{ my_list|first|upper }}

有些過濾器可以接受引數。過濾器的引數放在冒號之後,始終放在雙引號內。例如:

{{ bio|truncatewords:"30" }}

上述示例顯示 bio 變數的前 30 個詞。

下面是幾個最重要的過濾器。

• addslashes :在反斜線、單引號和雙引號前面新增一個反斜線。可用於轉義字串。例如: {{ val-
ue|addslashes }} 。
• date :根據引數中的格式字串格式化 date 或 datetime 物件。例如: {{ pub_date|date:"F j, Y"
}} 。格式字串在附錄 E 中說明。
• length :返回值的長度。對列表來說,返回元素的數量

。對字串來說,返回字元的數量。如果變數
未定義,返回 0 。

 Django Template Language(DTL)設計理念

Django 發端於線上新聞站點,其特點是大容量、變化頻繁。最初設計 Django 的人對 DTL 有非常明確的理念預設

如今,這些理念仍然是 Django 的核心。它們是:
1. 表現與邏輯分離
2. 避免重複
3. 與 HTML 解耦
4. XML 不好
5. 不要求具備設計能力
6. 透明處理空格
7. 不重造一門程式語言
8. 確保安全有保障
9. 可擴充套件

下面分述各點

1. 表現與邏輯分離

模板系統用於控制表現及與其相關的邏輯,僅此而已。超出這一基本目標的功能都不應該支援。

2. 避免重複

大多數動態網站都使用某種全站通用的設計,例如通用的頁頭、頁尾、導航欄,等等。Django 模板系
統應該為此提供便利的方式,把這些元素儲存在一個位置,減少重複的程式碼。模板繼承背後就是這個
理念。
3. 與 HTML 解耦

模板系統不應該只能輸出 HTML,還要能夠生成其他基於文字的格式(也就是純文字)。
4. XML 不應該作為模板語言

如果使用 XML 引擎解析模板,編輯模板時可能引入大量人為錯誤,而且處理模板有很多額外消耗。
5. 不要求具備設計能力

模板系統不應該必須在 WYSIWYG 編輯器(如 Dreamweaver)中才能寫出。這樣有太多侷限,句法不夠靈活。Django 的目標是讓模板編寫人員能直接編輯 HTML。
6. 透明處理空格

模板系統不應該特殊處理空格。模板中的空格就是空格,要像文字那樣顯示出來。不在模板標籤中的空格都應該顯示。
7. 不重造一門程式語言

模板系統一定不能允許:
◦ 為變數賦值
◦ 編寫高階的邏輯
也就是不能重造一門程式語言。模板系統的目標是提供適量的程式設計功能,例如分支和迴圈,足夠做表現相關的判斷就行。Django 模板系統知道模板最常由設計師編寫,而不是程式設計師,因此不要求具備 Python 知識。
8. 安全保障

模板系統預設應該禁止包含惡意程式碼,例如刪除資料庫記錄的命令。這是模板系統不允許隨意使用Python 程式碼的另一個原因。
9. 可擴充套件

模板系統應該認識到,高階模板編寫人員可能想擴充套件功能。這是自定義模板標籤和過濾器背後的理念。

在檢視中使用模板

模板載入機制

為了從檔案系統中載入模板,Django 提供了便利而強大的 API,力求去掉模板載入呼叫和模板自身的冗餘。若想使用這個模板載入 API,首先要告訴框架模板的儲存位置。這個位置在設定檔案中配置,即前一章介紹ROOT_URLCONF 設定時提到的 settings.py 檔案。開啟 settings.py 檔案,找到TEMPLATES設定。它的值是一個列表,分別針對各個模板引擎:

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [],
        'APP_DIRS': True,
        'OPTIONS': {
            # ... 一些選項 ...
        },
    },
]

 模板目錄

DIRS 的預設值是一個空列表。為了告訴 Django 的模板載入機制到哪裡尋找模板,選擇一個選儲存模板的目錄,把它新增到 DIRS 中,像下面這樣:

'DIRS': [
    '/home/html/example.com',
    '/home/html/default',
],

有幾點要注意:

• 如果不是構建沒有應用的極簡程式,最好留空 DIRS 。設定檔案預設把 APP_DIRS 設為 True ,因此最好在 Django 應用中放一個“templates”子目錄。
• 如果想在專案根目錄中放一些主模板(例如在 mysite/templates 目錄中),需要像這樣設定 DIRS :

'DIRS': [os.path.join(BASE_DIR, 'templates')],

• 模板目錄不一定非得叫 'templates' ,Django 不限制你用什麼名稱,但是堅守約定易於理解專案的結構。如果不想遵守這個約定,或者出於某些原因不這麼做,可以指定任何目錄,只要啟動 Web 伺服器的使用者有權讀取那個目錄中的模板就行
• 在 Windows 中要加上碟符,而且要使用 Unix 風格的正斜線,而不是反斜線,如下所示:

'DIRS': ['C:/www/django/templates']

回到 current_datetime 檢視,把它改成下面這樣:

from django.template.loader import get_template
from django.template import Context
from django.http import HttpResponse
import datetime
def current_datetime(request):
    now = datetime.datetime.now()
    t = get_template('current_datetime.html')
    html = t.render(Context({'current_date': now}))
    return HttpResponse(html)

為了找到模板在檔案系統中的位置, get_template() 按下列順序查詢:

• 如果 APP_DIRS 的值是 True ,而且使用 DTL,在當前應用中查詢“templates”目錄。
• 如果在當前應用中沒找到模板, get_template() 把傳給它的模板名稱新增到 DIRS 中的各個目錄後面,按順序在各個目錄中查詢。假如 DIRS 的第一個元素是 '/home/django/mysite/templates' ,上述get_template() 呼叫查詢的模板是 /home/django/mysite/templates/current_datetime.html 。
• 如果 get_template() 找不到指定名稱對應的模板,丟擲 TemplateDoesNotExist 異常。

接下來,建立 current_datetime.html 檔案,寫入下述模板程式碼:

It is now {{ current_date }}.

把這個檔案儲存到 mysite/templates 目錄中(如果沒有“templates”目錄,建立一個)。然後在 Web 瀏覽器中
重新整理頁面,此時應該能看到正確渲染的頁面。

目錄結構

setting.py修改

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [os.path.join(BASE_DIR, 'templates')],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]

views.py修改

def current_datetime(request):
    now = datetime.datetime.now()
    t = get_template('current_datetime.html')
    html = t.render({'current_date': now})
    return HttpResponse(html)

 current_datetime.html修改

It is now {{ current_date }}.

urls.py修改

urlpatterns = [
    url(r'^$', hello),
    url(r'^admin/', admin.site.urls),
    url(r'^hello/$',hello),
    url(r'^time/plus/(\d+)/$', hours_ahead),
    url(r'^time/', current_datetime),
]

執行結果

render()

下面是使用 render() 重寫的 current_datetime 檢視:

from django.shortcuts import render
import datetime
def current_datetime(request):
    now = datetime.datetime.now()
    return render(request, 'current_datetime.html', {'current_date': now})

關於這次改動:

• 不用再匯入 get_template 、 Template 、 Context 或 HttpResponse 了,而要匯入 django.shortcuts.ren-der 。 import datetime 不變。
• 在 current_datetime 函式中,仍然要計算 now ,不過載入模板、建立上下文、渲染模板和建立 HttpRe-sponse 物件全由 render() 呼叫代替了。 render() 的返回值是一個 HttpResponse 物件,因此在檢視中可以直接返回那個值。

render() 的第一個引數是請求物件,第二個引數是模板名稱,第三個單數可選,是一個欄位,用於建立傳給
模板的上下文。如果不指定第三個引數, render() 使用一個空字典。

模板子目錄

把模板放到模板目錄的子目錄中不是難事。呼叫 get_template() 時,只需在模板名稱前面加上子目錄的名稱和一條斜線,如下所示:

t = get_template('dateapp/current_datetime.html')

render() 是對 get_template() 的簡單包裝,因此在 render() 的第二個引數中也可以這麼做,如下所示:

return render(request, 'dateapp/current_datetime.html', {'current_date': now})

 include 模板標籤

這個標籤的作用是引入另一個模板的內容。它的引數是要引入的模板的名稱,可以是變數,也可以是硬編碼的字串(放在引號裡,單雙引號都行)。
只要想在多個模板中使用相同的程式碼,就可以考慮使用 {% include %} ,去除重複。下面兩個示例引入nav.html 模板的內容。二者的作用相同,目的是說既可以使用單引號,也可以使用雙引號。

{% include 'nav.html' %}
{% include "nav.html" %}

下述示例引入 includes/nav.html 模板的內容:

{% include 'includes/nav.html' %}

下述示例引入的模板名稱由變數 template_name 指定:

{% include template_name %}

如果 {% include %} 標籤的引數指定的模板不存在,Django 會做下面兩件事中的一件:

• DEBUG 為 True 時,渲染 Django 錯誤頁面,顯示 TemplateDoesNotExist 異常。
• DEBUG 為 False 時,靜默,那個標籤的位置什麼也不顯示。

模板繼承

目前,我們舉的示例都是簡短的 HTML 片段,但是在真實世界中,你將使用 Django 的模板系統建立整個HTML 頁面。這就引出 Web 開發常見的一個問題:在整個網站中,如何減少通用頁面區域(如全站導航)的重複和冗餘?

這裡用到一個你沒見過的模板標籤: {% block %} 。它的作用很簡單,告訴模板引擎,子模板可以覆蓋這部分
內容。

 base.html

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
<html lang="en">
<head>
<title>{% block title %}{% endblock %}</title>
</head>
<body>
<h1>My helpful timestamp site</h1>
{% block content %}{% endblock %}
{% block footer %}
<hr>
<p>Thanks for visiting my site.</p>
{% endblock %}
</body>
</html>

current_datetime.html

{% extends "base.html" %}
{% block title %}The current time{% endblock %}
{% block content %}
<p>It is now {{ current_date }}.</p>
{% endblock %}

原理

模板引擎載入 current_datetime.html 模板時,發現有 {% extends %} 標籤,意識到這是一個子模板,因此立即載入父模板,即這裡的 base.html 。

載入父模板時,模板引擎發現 base.html 中有三個 {% block %} 標籤,然後使用子模板中的內容替換。因此,將使用 {% block title %} 中定義的標題和 {% block content %} 中定義的內容。

注意,這個子模板沒有定義 footer 塊,因此模板系統使用父模板中的內容。父模板中的 {% block %} 塊總是後備內容

繼承不影響模板的上下文。也就是說,繼承樹中的任何模板都能訪問上下文中的每一個模板變數。根據需要,繼承層級的深度不限。繼承經常使用下述三層結構:

1. 建立 base.html 模板,定義網站的整體外觀。這個模板的內容很少變化。
2. 為網站中的各個“區域”建立 base_SECTION.html 模板(如 base_photos.html 和 base_forum.html )。這些模板擴充套件 base.html ,包含各區域專屬的樣式和設計。
3. 為各種頁面建立單獨的模板,例如論壇頁面或相簿。這些模板擴充套件相應的區域模板。

使用模板繼承的一些指導方針:

• 如果模板中有 {% extends %} ,必須是模板中的第一個標籤。否則,模板繼承不起作用。
• 一般來說,基模板中的 {% block %} 標籤越多越好。記住,子模板無需定義父模板中的全部塊,因此可以為一些塊定義合理的預設內容,只在子模板中覆蓋需要的塊。鉤子多總是好的。
• 如果發現要在多個模板中重複編寫相同的程式碼,或許說明應該把那些程式碼移到父模板中的一個 {%block %} 標籤裡。
• 如果需要從父模板中的塊裡獲取內容,使用 {{ block.super }} ,這是一個“魔法”變數,提供父模板中渲染後的文字。向塊中新增內容,而不是完全覆蓋時就可以這麼做。
• 在同一個模板中不能為多個 {% block %} 標籤定義相同的名稱。之所以有這個限制,是因為 block 標籤是雙向的。即, block 標籤不僅標識供填充的空位,還用於定義填充父模板中空位的內容。如果一個模板中有兩個同名的塊,那麼父模板就不知道使用哪個塊裡的內容。
• 傳給 {% extends %} 的模板名稱使用與 get_template() 相同的方法載入。即,模板在 DIRS 設定定義的目錄中,或者在當前 Django 應用的“templates”目錄裡。
• 多數情況下, {% extends %} 的引數是字串,不過如果直到執行時才知道父模板的名稱,也可以用變數。通過這一點可以做些動態判斷。