範本

作為一個網頁框架,Django 需要一種方便的方式來動態生成 HTML。最常見的方法是依賴範本。範本包含所需 HTML 輸出的靜態部分,以及一些描述如何插入動態內容的特殊語法。如需使用範本建立 HTML 頁面的實務範例,請參閱教學 3

可以使用一個或多個範本引擎(或者如果您不使用範本,則甚至可以是零個)來設定 Django 專案。Django 為其自己的範本系統(創造性地稱為 Django 範本語言 (DTL))和流行的替代方案 Jinja2 提供了內建後端。其他範本語言的後端可能可從第三方取得。您也可以編寫自己的自訂後端,請參閱自訂範本後端

Django 定義了一個標準 API,用於載入和渲染範本,而與後端無關。載入包括尋找給定識別碼的範本並對其進行預處理,通常是將其編譯為記憶體中的表示形式。渲染是指使用上下文資料來內插範本並傳回結果字串。

Django 範本語言是 Django 自己的範本系統。在 Django 1.8 之前,它是唯一可用的內建選項。它是一個不錯的範本庫,即使它相當固執己見並且有一些特殊的怪癖。如果您沒有迫切的理由選擇其他後端,您應該使用 DTL,尤其是當您編寫可外掛的應用程式並且打算發佈範本時。包含範本的 Django contrib 應用程式,例如django.contrib.admin,使用 DTL。

由於歷史原因,對範本引擎的一般支援和 Django 範本語言的實作都位於django.template命名空間中。

警告

範本系統對於不受信任的範本作者來說並不安全。例如,網站不應允許其使用者提供自己的範本,因為範本作者可以執行 XSS 攻擊和存取可能包含敏感資訊的範本變數的屬性等操作。

Django 範本語言

語法

關於本節

這是 Django 範本語言語法的概述。如需詳細資訊,請參閱語言語法參考

Django 範本是一個文字文件或使用 Django 範本語言標記的 Python 字串。某些結構會被範本引擎識別和解釋。主要的是變數和標籤。

範本使用內容來渲染。渲染會將變數替換為其值(這些值在內容中查找),並執行標籤。其他所有內容都會按原樣輸出。

Django 範本語言的語法包含四種結構。

變數

變數會從內容輸出一個值,該內容是一個類似字典的物件,將鍵對應到值。

變數用 {{}} 包圍,如下所示

My first name is {{ first_name }}. My last name is {{ last_name }}.

使用 {'first_name': 'John', 'last_name': 'Doe'} 的內容,此範本會渲染為

My first name is John. My last name is Doe.

字典查找、屬性查找和清單索引查找使用點表示法實作

{{ my_dict.key }}
{{ my_object.attribute }}
{{ my_list.0 }}

如果變數解析為可呼叫的變數,則範本系統會呼叫它,但不帶引數,並使用其結果而不是可呼叫的變數。

標籤

標籤在渲染過程中提供任意邏輯。

這個定義是故意含糊的。例如,標籤可以輸出內容、充當控制結構(例如「if」語句或「for」迴圈)、從資料庫中擷取內容,甚至啟用對其他範本標籤的存取。

標籤用 {%%} 包圍,如下所示

{% csrf_token %}

大多數標籤接受引數

{% cycle 'odd' 'even' %}

某些標籤需要開始和結束標籤

{% if user.is_authenticated %}Hello, {{ user.username }}.{% endif %}

有一個內建標籤的參考,以及撰寫自訂標籤的指示

篩選器

篩選器會轉換變數和標籤引數的值。

它們看起來像這樣

{{ django|title }}

使用 {'django': 'the web framework for perfectionists with deadlines'} 的內容,此範本會渲染為

The Web Framework For Perfectionists With Deadlines

某些篩選器會採用引數

{{ my_date|date:"Y-m-d" }}

有一個內建篩選器的參考,以及撰寫自訂篩選器的指示

註解

註解看起來像這樣

{# this won't be rendered #}

{% comment %} 標籤提供多行註解。

組件

關於本節

這是 Django 範本語言 API 的概述。如需詳細資訊,請參閱API 參考

引擎

django.template.Engine 封裝了 Django 範本系統的執行個體。直接實例化 Engine 的主要原因是,在 Django 專案之外使用 Django 範本語言。

django.template.backends.django.DjangoTemplates 是一個輕型包裝函式,可將 django.template.Engine 適應 Django 的範本後端 API。

範本

django.template.Template 代表已編譯的範本。範本是透過 Engine.get_template()Engine.from_string() 取得的。

同樣地,django.template.backends.django.Template 是一個輕型包裝函式,可將 django.template.Template 適應通用範本 API。

內容

django.template.Context 除了內容資料外,還會保留一些中繼資料。它會傳遞到 Template.render(),以用於渲染範本。

django.template.RequestContextContext 的子類別,它會儲存目前的 HttpRequest 並執行範本內容處理器。

通用 API 沒有等效的概念。內容資料會以純 dict 的形式傳遞,目前的 HttpRequest 會在需要時單獨傳遞。

載入器

樣板載入器負責尋找樣板、載入它們並返回 Template 物件。

Django 提供了數個內建樣板載入器,並支援自訂樣板載入器

Context processors

Context processors 是接收當前 HttpRequest 作為引數的函式,並回傳一個 dict 的資料,該資料將被加入到渲染上下文(rendering context)。

它們的主要用途是在不重複程式碼的情況下,將所有樣板共用的通用資料加入到上下文中。

Django 提供了許多內建的 context processors,您也可以實作自己的額外 context processors。

樣板引擎的支援

組態設定

樣板引擎是透過 TEMPLATES 設定來配置的。它是一個組態設定的列表,每個引擎一個設定。預設值為空。由 startproject 命令產生的 settings.py 定義了一個更有用的值。

TEMPLATES = [
    {
        "BACKEND": "django.template.backends.django.DjangoTemplates",
        "DIRS": [],
        "APP_DIRS": True,
        "OPTIONS": {
            # ... some options here ...
        },
    },
]

BACKEND 是一個指向實作 Django 樣板後端 API 的樣板引擎類別的 Python 點狀路徑。內建的後端是 django.template.backends.django.DjangoTemplatesdjango.template.backends.jinja2.Jinja2

由於大多數引擎是從檔案載入樣板,因此每個引擎的頂層組態都包含兩個常見的設定。

  • DIRS 定義了一個目錄列表,引擎應該按照搜尋順序在這些目錄中尋找樣板來源檔案。

  • APP_DIRS 設定是否讓引擎在已安裝應用程式內尋找樣板。每個後端都會為應用程式內應該儲存其樣板的子目錄定義一個慣用名稱。

雖然不常見,但可以使用不同的選項配置同一個後端的幾個實例。在這種情況下,您應該為每個引擎定義一個唯一的 NAME

OPTIONS 包含後端特定的設定。

用法

django.template.loader 模組定義了兩個用於載入樣板的函式。

get_template(template_name, using=None)[來源]

這個函式載入指定名稱的樣板,並返回一個 Template 物件。

回傳值的確切類型取決於載入樣板的後端。每個後端都有自己的 Template 類別。

get_template() 會依序嘗試每個樣板引擎,直到其中一個成功為止。如果找不到樣板,它會引發 TemplateDoesNotExist。如果找到樣板但包含無效語法,它會引發 TemplateSyntaxError

樣板的搜尋和載入方式取決於每個引擎的後端和組態。

如果您想要將搜尋範圍限制在特定的樣板引擎,請在 using 引數中傳遞引擎的 NAME

select_template(template_name_list, using=None)[來源]

select_template()get_template() 非常相似,只是它接受一個樣板名稱的列表。它會依序嘗試每個名稱,並返回第一個存在的樣板。

如果載入樣板失敗,可能會引發以下在 django.template 中定義的兩個例外狀況:

exception TemplateDoesNotExist(msg, tried=None, backend=None, chain=None)[來源]

當找不到樣板時,會引發此例外。它接受以下選用引數,以便在除錯頁面上填入樣板剖析報告

backend

產生此例外的樣板後端實例。

tried

在尋找樣板時嘗試過的來源列表。格式為包含 (origin, status) 的元組列表,其中 origin 是一個類似來源 的物件,而 status 是一個表示找不到樣板的原因的字串。

chain

嘗試載入樣板時引發的中繼 TemplateDoesNotExist 例外列表。此列表被諸如 get_template() 等函式使用,這些函式會嘗試從多個引擎載入給定的樣板。

exception TemplateSyntaxError(msg)[來源]

當找到樣板但包含錯誤時,會引發此例外。

get_template()select_template() 返回的 Template 物件必須提供一個具有以下簽章的 render() 方法:

Template.render(context=None, request=None)

使用給定的上下文渲染此樣板。

如果提供了 context,則它必須是一個 dict。如果沒有提供,引擎將使用空上下文渲染樣板。

如果提供了 request,則它必須是一個 HttpRequest。然後,引擎必須將其以及 CSRF token 提供給樣板。如何實現這一點取決於每個後端。

以下是一個搜尋演算法的範例。在此範例中,TEMPLATES 設定為:

TEMPLATES = [
    {
        "BACKEND": "django.template.backends.django.DjangoTemplates",
        "DIRS": [
            "/home/html/example.com",
            "/home/html/default",
        ],
    },
    {
        "BACKEND": "django.template.backends.jinja2.Jinja2",
        "DIRS": [
            "/home/html/jinja2",
        ],
    },
]

如果您呼叫 get_template('story_detail.html'),以下是 Django 將依序搜尋的檔案:

  • /home/html/example.com/story_detail.html ('django' 引擎)

  • /home/html/default/story_detail.html ('django' 引擎)

  • /home/html/jinja2/story_detail.html ('jinja2' 引擎)

如果您呼叫 select_template(['story_253_detail.html', 'story_detail.html']),以下是 Django 將搜尋的內容:

  • /home/html/example.com/story_253_detail.html ('django' 引擎)

  • /home/html/default/story_253_detail.html ('django' 引擎)

  • /home/html/jinja2/story_253_detail.html ('jinja2' 引擎)

  • /home/html/example.com/story_detail.html ('django' 引擎)

  • /home/html/default/story_detail.html ('django' 引擎)

  • /home/html/jinja2/story_detail.html ('jinja2' 引擎)

當 Django 找到存在的樣板時,它會停止搜尋。

使用 django.template.loader.select_template() 以獲得更大的彈性。

您可以使用 select_template() 進行彈性的樣板載入。例如,如果您寫了一篇新聞報導,並希望某些報導使用自訂樣板,可以使用類似 select_template(['story_%s_detail.html' % story.id, 'story_detail.html']) 的程式碼。這樣您就可以為個別報導使用自訂樣板,並為沒有自訂樣板的報導使用備用樣板。

在每個包含樣板的目錄中,將樣板組織在子目錄中是可行且較佳的做法。慣例是為每個 Django 應用程式建立一個子目錄,並在這些子目錄中根據需要建立更多子目錄。

為了您自己好,請這樣做。將所有樣板儲存在單一目錄的根目錄層級會變得雜亂無章。

若要載入子目錄中的樣板,請使用斜線,如下所示:

get_template("news/story_detail.html")

使用與上述相同的 TEMPLATES 選項,這將嘗試載入以下樣板:

  • /home/html/example.com/news/story_detail.html ( 'django' 引擎 )

  • /home/html/default/news/story_detail.html ( 'django' 引擎 )

  • /home/html/jinja2/news/story_detail.html ( 'jinja2' 引擎 )

此外,為了減少重複載入和渲染樣板的繁瑣,Django 提供了一個快捷函式來自動化此過程。

render_to_string(template_name, context=None, request=None, using=None)[原始碼]

render_to_string() 載入樣板的方式與 get_template() 相同,並立即呼叫其 render() 方法。它接受以下引數。

template_name

要載入和渲染的樣板名稱。如果它是樣板名稱的清單,Django 會使用 select_template() 而不是 get_template() 來尋找樣板。

context

一個 dict,用作渲染樣板的內容。

request

一個可選的 HttpRequest,在樣板的渲染過程中可用。

using

一個可選的樣板引擎 NAME。樣板的搜尋將僅限於該引擎。

使用範例

from django.template.loader import render_to_string

rendered = render_to_string("my_template.html", {"foo": "bar"})

另請參閱 render() 快捷方式,它會呼叫 render_to_string(),並將結果饋入 HttpResponse 中,適合從檢視中傳回。

最後,您可以直接使用已設定的引擎

engines

樣板引擎可在 django.template.engines 中找到

from django.template import engines

django_engine = engines["django"]
template = django_engine.from_string("Hello {{ name }}!")

查閱鍵 — 在此範例中為 'django' — 是引擎的 NAME

內建後端

class DjangoTemplates[原始碼]

BACKEND 設定為 'django.template.backends.django.DjangoTemplates' 以設定 Django 樣板引擎。

APP_DIRSTrue 時,DjangoTemplates 引擎會在已安裝應用程式的 templates 子目錄中尋找樣板。此通用名稱保留用於向後相容性。

DjangoTemplates 引擎接受以下 OPTIONS

  • 'autoescape':一個布林值,控制是否啟用 HTML 自動跳脫。

    預設值為 True

    警告

    僅在您渲染非 HTML 樣板時,才將其設定為 False

  • 'context_processors':一個虛線 Python 路徑清單,指向用於在樣板使用請求渲染時,填入內容的可呼叫物件。這些可呼叫物件會將請求物件作為引數,並傳回要合併到內容中的項目 dict

    預設值為空清單。

    如需更多資訊,請參閱 RequestContext

  • 'debug':一個布林值,可開啟/關閉樣板除錯模式。如果它是 True,則精美的錯誤頁面將顯示在樣板渲染期間引發的任何例外狀況的詳細報告。此報告包含具有相應行突出顯示的相關樣板片段。

    預設值為 DEBUG 設定的值。

  • 'loaders':一個虛線 Python 路徑清單,指向樣板載入器類別。每個 Loader 類別都知道如何從特定來源匯入樣板。或者,可以使用元組代替字串。元組中的第一個項目應該是 Loader 類別名稱,後續項目會傳遞給初始化期間的 Loader

    預設值取決於 DIRSAPP_DIRS 的值。

    如需詳細資訊,請參閱 載入器類型

  • 'string_if_invalid':當變數無效時(例如拼寫錯誤),樣板系統應使用的字串輸出。

    預設值為空字串。

    如需詳細資訊,請參閱 如何處理無效變數

  • 'file_charset':用於讀取磁碟上的樣板檔案的字元集。

    預設值為 'utf-8'

  • 'libraries':一個標籤字典,以及要向樣板引擎註冊的樣板標籤模組的虛線 Python 路徑。這可以用於新增程式庫或為現有程式庫提供替代標籤。例如:

    OPTIONS = {
        "libraries": {
            "myapp_tags": "path.to.myapp.tags",
            "admin.urls": "django.contrib.admin.templatetags.admin_urls",
        },
    }
    

    可以透過將對應的字典鍵傳遞給 {% load %} 標籤來載入程式庫。

  • 'builtins':一個虛線 Python 路徑清單,指向要新增到 內建模組 的樣板標籤模組。例如:

    OPTIONS = {
        "builtins": ["myapp.builtins"],
    }
    

    可以使用內建程式庫中的標籤和篩選器,而無需先呼叫 {% load %} 標籤。

class Jinja2[原始碼]

需要安裝 Jinja2

$ python -m pip install Jinja2
...\> py -m pip install Jinja2

BACKEND 設定為 'django.template.backends.jinja2.Jinja2' 以設定 Jinja2 引擎。

APP_DIRS 設定為 True 時,Jinja2 引擎會在已安裝應用程式的 jinja2 子目錄中尋找範本。

OPTIONS 中最重要的條目是 'environment'。它是一個以點分隔的 Python 路徑,指向一個返回 Jinja2 環境的可調用物件。預設值為 'jinja2.Environment'。Django 會調用該可調用物件,並將其他選項作為關鍵字引數傳遞。此外,Django 還為一些選項增加了與 Jinja2 不同的預設值。

  • 'autoescape': True

  • 'loader': 為 DIRSAPP_DIRS 配置的載入器

  • 'auto_reload': settings.DEBUG

  • 'undefined': DebugUndefined if settings.DEBUG else Undefined

Jinja2 引擎也接受以下 OPTIONS

  • 'context_processors':一個虛線 Python 路徑清單,指向用於在樣板使用請求渲染時,填入內容的可呼叫物件。這些可呼叫物件會將請求物件作為引數,並傳回要合併到內容中的項目 dict

    預設值為空清單。

    不建議將上下文處理器與 Jinja2 範本一起使用。

    上下文處理器在 Django 範本中很有用,因為 Django 範本不支援使用引數調用函式。由於 Jinja2 沒有此限制,因此建議將您將用作上下文處理器的函式放入可供範本使用的全域變數中,方法是使用如下所述的 jinja2.Environment。然後,您可以在範本中調用該函式。

    {{ function(request) }}
    

    一些 Django 範本上下文處理器會返回一個固定值。對於 Jinja2 範本,由於您可以直接在 jinja2.Environment 中新增常數,因此不需要此間接層。

    為 Jinja2 新增上下文處理器的原始用例涉及:

    • 執行依賴於請求的昂貴計算。

    • 每個範本都需要結果。

    • 在每個範本中多次使用結果。

    除非滿足所有這些條件,否則將函式傳遞給範本更符合 Jinja2 的設計理念。

預設配置盡可能保持最小化。如果範本是使用請求呈現的(例如,使用 render() 時),Jinja2 後端會將全域變數 requestcsrf_inputcsrf_token 新增到上下文中。除此之外,此後端不會建立 Django 風格的環境。它不知道 Django 的篩選器和標籤。為了使用 Django 特定的 API,您必須將它們配置到環境中。

例如,您可以建立具有以下內容的 myproject/jinja2.py

from django.templatetags.static import static
from django.urls import reverse

from jinja2 import Environment


def environment(**options):
    env = Environment(**options)
    env.globals.update(
        {
            "static": static,
            "url": reverse,
        }
    )
    return env

並將 'environment' 選項設定為 'myproject.jinja2.environment'

然後,您可以在 Jinja2 範本中使用以下結構

<img src="{{ static('path/to/company-logo.png') }}" alt="Company Logo">

<a href="{{ url('admin:index') }}">Administration</a>

標籤和篩選器的概念同時存在於 Django 範本語言和 Jinja2 中,但它們的使用方式不同。由於 Jinja2 支援將引數傳遞給範本中的可調用物件,因此許多在 Django 範本中需要範本標籤或篩選器的功能,可以通過調用 Jinja2 範本中的函式來實現,如以上範例所示。Jinja2 的全域命名空間消除了對範本上下文處理器的需求。Django 範本語言沒有與 Jinja2 測試等效的功能。

返回頂部