Django 樣板語言:給 Python 程式設計師

本文從技術角度解釋 Django 樣板系統 – 其運作方式以及如何擴展它。如果您正在尋找有關語言語法的參考資料,請參閱 Django 樣板語言

本文假設您了解樣板、上下文、變數、標籤和渲染。如果您不熟悉這些概念,請從 Django 樣板語言簡介 開始。

概觀

在 Python 中使用樣板系統是一個三步驟流程

  1. 您設定一個 Engine

  2. 您將樣板程式碼編譯為一個 Template

  3. 您使用一個 Context 渲染樣板。

Django 專案通常依賴於這些步驟的 高階、與後端無關的 API,而不是樣板系統的較低階 API。

  1. 對於 TEMPLATES 設定中的每個 DjangoTemplates 後端,Django 都會實例化一個 EngineDjangoTemplates 包裝 Engine 並將其調整為通用的樣板後端 API。

  2. django.template.loader 模組提供諸如 get_template() 等函式來載入樣板。它們會傳回一個 django.template.backends.django.Template,它會包裝實際的 django.template.Template

  3. 在前一個步驟中取得的 Template 有一個 render() 方法,它會將上下文和可能的請求編組到一個 Context,並將渲染委派給底層的 Template

設定引擎

如果您正在使用 DjangoTemplates 後端,那麼這可能不是您要找的文件。使用該後端的 engine 屬性可以存取下面描述的 Engine 類別的實例,並且下面提到的任何屬性預設值都會被 DjangoTemplates 傳遞的值覆寫。

class Engine(dirs=None, app_dirs=False, context_processors=None, debug=False, loaders=None, string_if_invalid='', file_charset='utf-8', libraries=None, builtins=None, autoescape=True)[原始碼]

在實例化 Engine 時,所有引數都必須作為關鍵字引數傳遞

  • dirs 是一個目錄列表,引擎應在其中尋找樣板原始檔。它用於設定 filesystem.Loader

    預設為空列表。

  • app_dirs 只會影響 loaders 的預設值。請參閱下文。

    預設為 False

  • autoescape 控制是否啟用 HTML 自動跳脫。

    預設為 True

    警告

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

  • context_processors 是一個以點分隔的 Python 路徑列表,指向可用於在以請求渲染樣板時填入上下文的可呼叫物件。這些可呼叫物件會接收一個請求物件作為其引數,並傳回一個 dict,其中包含要合併到上下文中的項目。

    預設為空列表。

    有關更多資訊,請參閱 RequestContext

  • debug 是一個布林值,用於開啟/關閉樣板偵錯模式。如果它是 True,樣板引擎將儲存額外的偵錯資訊,這些資訊可用於顯示在樣板渲染期間引發的任何例外狀況的詳細報告。

    預設為 False

  • loaders 是一個樣板載入器類別列表,指定為字串。每個 Loader 類別都知道如何從特定來源匯入樣板。或者,可以使用元組而不是字串。元組中的第一個項目應該是 Loader 類別名稱,後續項目將在初始化期間傳遞給 Loader

    預設為包含的列表

    • 'django.template.loaders.filesystem.Loader'

    • 僅當 app_dirsTrue 時,才為 'django.template.loaders.app_directories.Loader'

    然後,這些載入器會被包裝在 django.template.loaders.cached.Loader 中。

    有關詳細資訊,請參閱 載入器類型

  • string_if_invalid 是樣板系統應該用於無效(例如,拼寫錯誤)變數的輸出(作為字串)。

    預設為空字串。

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

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

    預設為 'utf-8'

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

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

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

  • 'builtins':一個以點分隔的 Python 路徑列表,指向要新增到 內建 的樣板標籤模組。例如

    Engine(
        builtins=["myapp.builtins"],
    )
    

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

static Engine.get_default()[原始碼]

從第一個已設定的 Engine 引擎回傳底層的 Engine。如果沒有設定任何引擎,則會引發 ImproperlyConfigured 錯誤。

這是為了保留依賴全域可用、隱式設定引擎的 API 所必需的。強烈建議不要用於其他用途。

Engine.from_string(template_code)[原始碼]

編譯給定的樣板程式碼,並回傳一個 Template 物件。

Engine.get_template(template_name)[原始碼]

載入具有給定名稱的樣板,編譯它並回傳一個 Template 物件。

Engine.select_template(template_name_list)[原始碼]

類似於 get_template(),但它接受名稱列表並回傳找到的第一個樣板。

載入樣板

建立 Template 的建議方式是呼叫 Engine 的工廠方法:get_template()select_template()from_string()

在 Django 專案中,如果 TEMPLATES 設定定義了 DjangoTemplates 引擎,則可以直接實例化一個 Template。如果定義了多個 DjangoTemplates 引擎,則將使用第一個。

class Template[原始碼]

此類別位於 django.template.Template。建構函式接受一個參數 — 原始樣板程式碼

from django.template import Template

template = Template("My name is {{ my_name }}.")

幕後機制

系統只會解析您的原始樣板程式碼一次 – 當您建立 Template 物件時。從那時起,它會在內部儲存為效能考量的樹狀結構。

即使解析本身也相當快速。大多數解析是透過對單個、簡短的正規表示式進行單次呼叫來完成的。

呈現 Context

一旦您有一個已編譯的 Template 物件,您就可以使用它來呈現 Context。您可以重複使用相同的樣板,以不同的 Context 多次呈現它。

class Context(dict_=None, autoescape=True, use_l10n=None, use_tz=None)[原始碼]

django.template.Context 的建構函式接受一個可選參數 — 將變數名稱對應到變數值的字典。

也可以指定三個可選的關鍵字引數

  • autoescape 控制是否啟用 HTML 自動跳脫。

    預設為 True

    警告

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

  • use_l10n 覆寫預設是否會本地化值。如果設定為 True,數字和日期將根據地區設定格式化。

    預設值為 None

    有關詳細資訊,請參閱 在樣板中控制本地化

  • use_tz 覆寫日期在樣板中呈現時是否轉換為當地時間。如果設定為 True,所有日期都將使用當地時區呈現。這優先於 USE_TZ

    預設值為 None

    有關詳細資訊,請參閱 樣板中具時區意識的輸出

有關使用範例,請參閱下方的 使用 Context 物件

Template.render(context)[原始碼]

呼叫 Template 物件的 render() 方法,並傳入一個 Context 來「填入」樣板

>>> from django.template import Context, Template
>>> template = Template("My name is {{ my_name }}.")

>>> context = Context({"my_name": "Adrian"})
>>> template.render(context)
"My name is Adrian."

>>> context = Context({"my_name": "Dolores"})
>>> template.render(context)
"My name is Dolores."

變數和查詢

變數名稱必須包含任何字母 (A-Z)、任何數字 (0-9)、底線(但它們不得以底線開頭)或點號。

點號在樣板呈現中具有特殊含義。變數名稱中的點號表示查詢。具體而言,當樣板系統在變數名稱中遇到點號時,它會依此順序嘗試以下查詢:

  • 字典查詢。範例:foo["bar"]

  • 屬性查詢。範例:foo.bar

  • 清單索引查詢。範例:foo[bar]

請注意,如果樣板 Context 中存在變數 “bar”,則在像 {{ foo.bar }} 的樣板運算式中,「bar」會被解讀為文字字串,而不是使用變數 “bar” 的值。

樣板系統會使用第一個有效的查詢類型。它是短路邏輯。以下是一些範例:

>>> from django.template import Context, Template
>>> t = Template("My name is {{ person.first_name }}.")
>>> d = {"person": {"first_name": "Joe", "last_name": "Johnson"}}
>>> t.render(Context(d))
"My name is Joe."

>>> class PersonClass:
...     pass
...
>>> p = PersonClass()
>>> p.first_name = "Ron"
>>> p.last_name = "Nasty"
>>> t.render(Context({"person": p}))
"My name is Ron."

>>> t = Template("The first stooge in the list is {{ stooges.0 }}.")
>>> c = Context({"stooges": ["Larry", "Curly", "Moe"]})
>>> t.render(c)
"The first stooge in the list is Larry."

如果變數的任何部分是可呼叫的,則樣板系統會嘗試呼叫它。範例:

>>> class PersonClass2:
...     def name(self):
...         return "Samantha"
...
>>> t = Template("My name is {{ person.name }}.")
>>> t.render(Context({"person": PersonClass2}))
"My name is Samantha."

可呼叫變數比只需要直接查詢的變數稍微複雜一些。以下是一些需要記住的事項:

  • 如果變數在呼叫時引發例外,則該例外將會傳播,除非該例外具有一個屬性 silent_variable_failure,其值為 True。如果例外確實具有一個 silent_variable_failure 屬性,其值為 True,則變數將呈現為引擎 string_if_invalid 設定選項的值(預設為空字串)。範例:

    >>> t = Template("My name is {{ person.first_name }}.")
    >>> class PersonClass3:
    ...     def first_name(self):
    ...         raise AssertionError("foo")
    ...
    >>> p = PersonClass3()
    >>> t.render(Context({"person": p}))
    Traceback (most recent call last):
    ...
    AssertionError: foo
    
    >>> class SilentAssertionError(Exception):
    ...     silent_variable_failure = True
    ...
    >>> class PersonClass4:
    ...     def first_name(self):
    ...         raise SilentAssertionError
    ...
    >>> p = PersonClass4()
    >>> t.render(Context({"person": p}))
    "My name is ."
    

    請注意,django.core.exceptions.ObjectDoesNotExist 是所有 Django 資料庫 API DoesNotExist 例外的基底類別,它具有 silent_variable_failure = True。因此,如果您將 Django 樣板與 Django 模型物件一起使用,任何 DoesNotExist 例外都會靜默失敗。

  • 只有在變數沒有需要的引數時才能呼叫它。否則,系統將回傳引擎的 string_if_invalid 選項的值。

  • 呼叫某些變數可能會產生副作用,並且允許樣板系統存取它們既愚蠢又會產生安全漏洞。

    一個很好的例子是每個 Django 模型物件上的 delete() 方法。不應該允許樣板系統執行類似以下的操作:

    I will now delete this valuable data. {{ data.delete }}
    

    為了避免這種情況,請在可呼叫變數上設定 alters_data 屬性。如果變數設定了 alters_data=True,範本系統就不會呼叫該變數,而是無條件地將變數替換為 string_if_invalid。Django 模型物件上動態產生的 delete()save() 方法會自動設定 alters_data=True。範例:

    def sensitive_function(self):
        self.database_record.delete()
    
    
    sensitive_function.alters_data = True
    
  • 有時,您可能因為其他原因想要關閉此功能,並告知範本系統無論如何都不要呼叫變數。若要這麼做,請在可呼叫變數上設定一個 do_not_call_in_templates 屬性,值為 True。這樣範本系統就會像對待不可呼叫的變數一樣(例如,讓您可以存取該可呼叫變數的屬性)。

如何處理無效變數

一般而言,如果變數不存在,範本系統會插入引擎的 string_if_invalid 設定選項的值,預設值為 '' (空字串)。

只有當 string_if_invalid 設定為 '' (空字串)時,套用至無效變數的篩選器才會被套用。如果 string_if_invalid 設定為任何其他值,變數篩選器將會被忽略。

對於 ifforregroup 範本標籤,此行為略有不同。如果將無效變數提供給這些範本標籤之一,該變數將會被視為 None。篩選器會始終套用至這些範本標籤內的無效變數。

如果 string_if_invalid 包含 '%s',格式標記將會被替換為無效變數的名稱。

僅限於除錯目的!

雖然 string_if_invalid 可以是一個有用的除錯工具,但將其作為「開發預設值」開啟並不是一個好主意。

許多範本,包括 Django 的一些範本,都依賴於範本系統在遇到不存在的變數時保持靜默。如果您將 string_if_invalid 的值設為 '' 以外的值,您將會在這些範本和網站上遇到渲染問題。

一般而言,string_if_invalid 應該只在除錯特定的範本問題時才啟用,並在除錯完成後清除。

內建變數

每個內容都包含 TrueFalseNone。正如您所預期的,這些變數會解析為相應的 Python 物件。

字串常值的限制

Django 的範本語言無法跳脫其自身語法所使用的字元。例如,如果您需要輸出像 {%%} 這樣的字元序列,則需要使用 templatetag 標籤。

如果您想要在範本篩選器或標籤引數中包含這些序列,也會出現類似的問題。例如,當剖析區塊標籤時,Django 的範本剖析器會在 {% 之後尋找第一次出現的 %}。這會阻止使用 "%}" 作為字串常值。例如,以下運算式會引發 TemplateSyntaxError

{% include "template.html" tvar="Some string literal with %} in it." %}

{% with tvar="Some string literal with %} in it." %}{% endwith %}

在篩選器引數中使用保留序列也會觸發相同的問題

{{ some.variable|default:"}}" }}

如果您需要使用包含這些序列的字串,請將它們儲存在範本變數中,或使用自訂範本標籤或篩選器來解決這個限制。

使用 Context 物件

大多數情況下,您會透過將完整填入的字典傳遞給 Context() 來實例化 Context 物件。但是,您也可以在使用標準字典語法實例化 Context 物件後,從中新增和刪除項目

>>> from django.template import Context
>>> c = Context({"foo": "bar"})
>>> c["foo"]
'bar'
>>> del c["foo"]
>>> c["foo"]
Traceback (most recent call last):
...
KeyError: 'foo'
>>> c["newvariable"] = "hello"
>>> c["newvariable"]
'hello'
Context.get(key, otherwise=None)

如果 key 在內容中,則傳回 key 的值,否則傳回 otherwise

Context.setdefault(key, default=None)

如果 key 在內容中,則傳回其值。否則,插入 key,值為 default,並傳回 default

Context.pop()
Context.push()
exception ContextPopException[來源]

Context 物件是一個堆疊。也就是說,您可以 push()pop() 它。如果您 pop() 過多,它會引發 django.template.ContextPopException

>>> c = Context()
>>> c["foo"] = "first level"
>>> c.push()
{}
>>> c["foo"] = "second level"
>>> c["foo"]
'second level'
>>> c.pop()
{'foo': 'second level'}
>>> c["foo"]
'first level'
>>> c["foo"] = "overwritten"
>>> c["foo"]
'overwritten'
>>> c.pop()
Traceback (most recent call last):
...
ContextPopException

您也可以使用 push() 作為內容管理器,以確保呼叫對應的 pop()

>>> c = Context()
>>> c["foo"] = "first level"
>>> with c.push():
...     c["foo"] = "second level"
...     c["foo"]
...
'second level'
>>> c["foo"]
'first level'

傳遞給 push() 的所有引數都會傳遞給用於建構新內容層級的 dict 建構函式。

>>> c = Context()
>>> c["foo"] = "first level"
>>> with c.push(foo="second level"):
...     c["foo"]
...
'second level'
>>> c["foo"]
'first level'
Context.update(other_dict)[來源]

除了 push()pop() 之外,Context 物件還定義了 update() 方法。它的作用類似於 push(),但是它會接收一個字典作為引數,並將該字典推送至堆疊上,而不是一個空字典。

>>> c = Context()
>>> c["foo"] = "first level"
>>> c.update({"foo": "updated"})
{'foo': 'updated'}
>>> c["foo"]
'updated'
>>> c.pop()
{'foo': 'updated'}
>>> c["foo"]
'first level'

push() 一樣,您可以使用 update() 作為內容管理器,以確保呼叫對應的 pop()

>>> c = Context()
>>> c["foo"] = "first level"
>>> with c.update({"foo": "second level"}):
...     c["foo"]
...
'second level'
>>> c["foo"]
'first level'

某些自訂範本標籤 中,將 Context 用作堆疊非常方便。

Context.flatten()

透過使用 flatten() 方法,您可以將整個 Context 堆疊取得為一個包含內建變數的字典。

>>> c = Context()
>>> c["foo"] = "first level"
>>> c.update({"bar": "second level"})
{'bar': 'second level'}
>>> c.flatten()
{'True': True, 'None': None, 'foo': 'first level', 'False': False, 'bar': 'second level'}

內部也會使用 flatten() 方法來使 Context 物件具有可比較性。

>>> c1 = Context()
>>> c1["foo"] = "first level"
>>> c1["bar"] = "second level"
>>> c2 = Context()
>>> c2.update({"bar": "second level", "foo": "first level"})
{'foo': 'first level', 'bar': 'second level'}
>>> c1 == c2
True

在單元測試中,flatten() 的結果可以用來比較 Contextdict

class ContextTest(unittest.TestCase):
    def test_against_dictionary(self):
        c1 = Context()
        c1["update"] = "value"
        self.assertEqual(
            c1.flatten(),
            {
                "True": True,
                "None": None,
                "False": False,
                "update": "value",
            },
        )

使用 RequestContext

class RequestContext(request, dict_=None, processors=None, use_l10n=None, use_tz=None, autoescape=True)[原始碼]

Django 提供了一個特殊的 Context 類別,django.template.RequestContext,其行為與一般的 django.template.Context 略有不同。第一個不同之處在於它將 HttpRequest 作為其第一個參數。例如:

c = RequestContext(
    request,
    {
        "foo": "bar",
    },
)

第二個不同之處在於,它會根據引擎的 context_processors 設定選項自動填充 context 一些變數。

context_processors 選項是一個可呼叫物件的列表(稱為 **context processors**),這些物件會將請求物件作為其參數,並傳回一個字典,其中的項目會合併到 context 中。在預設產生的設定檔中,預設的 template engine 包含以下 context processors:

[
    "django.template.context_processors.debug",
    "django.template.context_processors.request",
    "django.contrib.auth.context_processors.auth",
    "django.contrib.messages.context_processors.messages",
]

除了這些之外,RequestContext 總是會啟用 'django.template.context_processors.csrf'。這是一個與安全性相關的 context processor,admin 和其他 contrib 應用程式需要用到它,並且為了避免意外的錯誤設定,它被刻意地硬編碼在其中,無法在 context_processors 選項中關閉。

每個 processor 都會依序套用。這表示如果一個 processor 將變數新增到 context 中,而第二個 processor 新增了具有相同名稱的變數,則第二個 processor 會覆寫第一個。預設的 processors 解釋如下。

何時套用 context processors

Context processors 是在 context 資料之上套用的。這表示 context processor 可能會覆寫您提供給 ContextRequestContext 的變數,因此請注意避免與 context processors 提供的變數名稱重疊。

如果您希望 context 資料優先於 context processors,請使用以下模式:

from django.template import RequestContext

request_context = RequestContext(request)
request_context.push({"my_name": "Adrian"})

Django 這樣做的目的是允許 context 資料在諸如 render()TemplateResponse 之類的 API 中覆寫 context processors。

此外,您可以使用可選的第三個位置參數 processors,為 RequestContext 提供額外的 processor 列表。在這個範例中,RequestContext 實例會取得一個 ip_address 變數:

from django.http import HttpResponse
from django.template import RequestContext, Template


def ip_address_processor(request):
    return {"ip_address": request.META["REMOTE_ADDR"]}


def client_ip_view(request):
    template = Template("{{ title }}: {{ ip_address }}")
    context = RequestContext(
        request,
        {
            "title": "Your IP Address",
        },
        [ip_address_processor],
    )
    return HttpResponse(template.render(context))

內建的 template context processors

以下是每個內建 processors 的功能:

django.contrib.auth.context_processors.auth

auth(request)[原始碼]

如果啟用此 processor,則每個 RequestContext 將包含以下變數:

  • user – 代表目前登入使用者的 auth.User 實例(如果客戶端未登入,則為 AnonymousUser 實例)。

  • permsdjango.contrib.auth.context_processors.PermWrapper 的實例,代表目前登入使用者擁有的權限。

django.template.context_processors.debug

debug(request)[原始碼]

如果啟用此 processor,則每個 RequestContext 將包含以下兩個變數 - 但前提是您的 DEBUG 設定設為 True,並且請求的 IP 位址(request.META['REMOTE_ADDR'])在 INTERNAL_IPS 設定中。

  • debugTrue。您可以在 template 中使用它來測試是否處於 DEBUG 模式。

  • sql_queries{'sql': ..., 'time': ...} 字典的列表,表示到目前為止在請求期間發生的每個 SQL 查詢及其所花費的時間。該列表按資料庫別名排序,然後按查詢排序。它會在存取時延遲產生。

django.template.context_processors.i18n

i18n(request)[原始碼]

如果啟用此 processor,則每個 RequestContext 將包含以下變數:

  • LANGUAGESLANGUAGES 設定的值。

  • LANGUAGE_BIDI – 如果目前的語言是從右到左的語言(例如:希伯來語、阿拉伯語),則為 True。如果它是從左到右的語言(例如:英語、法語、德語),則為 False

  • LANGUAGE_CODE – 如果存在 request.LANGUAGE_CODE。否則,為 LANGUAGE_CODE 設定的值。

請參閱 i18n template tags,以取得產生相同值的 template tags。

django.template.context_processors.media

如果啟用此 processor,則每個 RequestContext 將包含一個變數 MEDIA_URL,提供 MEDIA_URL 設定的值。

django.template.context_processors.static

static(request)[原始碼]

如果啟用此處理器,每個 RequestContext 都會包含一個變數 STATIC_URL,提供 STATIC_URL 設定的值。

django.template.context_processors.csrf

此處理器會新增一個 token,該 token 是 csrf_token 模板標籤為了防止 跨網站請求偽造 所需的。

django.template.context_processors.request

如果啟用此處理器,每個 RequestContext 都會包含一個變數 request,它是目前的 HttpRequest

django.template.context_processors.tz

tz(request)[原始碼]

如果啟用此處理器,每個 RequestContext 都會包含一個變數 TIME_ZONE,提供目前啟用的時區名稱。

django.contrib.messages.context_processors.messages

如果啟用此處理器,每個 RequestContext 都會包含這兩個變數

  • messages – 一個透過 訊息框架 設定的訊息清單(以字串形式)。

  • DEFAULT_MESSAGE_LEVELS – 訊息等級名稱到數值的對應。

撰寫您自己的內容處理器

內容處理器有一個簡單的介面:它是一個 Python 函式,接受一個引數,即 HttpRequest 物件,並回傳一個字典,該字典會被新增到模板內容中。

例如,要將 DEFAULT_FROM_EMAIL 設定新增到每個內容中

from django.conf import settings


def from_email(request):
    return {
        "DEFAULT_FROM_EMAIL": settings.DEFAULT_FROM_EMAIL,
    }

自訂內容處理器可以位於程式碼庫中的任何位置。Django 在意的是,您的自訂內容處理器是否已在 TEMPLATES 設定中的 'context_processors' 選項中指向,或者如果您直接使用 Engine,則是在 context_processors 引數中指向。

載入模板

一般來說,您會將模板儲存在檔案系統上的檔案中,而不是自行使用低階的 Template API。將模板儲存在指定為**模板目錄**的目錄中。

Django 會在多個位置搜尋模板目錄,這取決於您的模板載入設定(請參閱下方的「載入器類型」),但指定模板目錄最基本的方式是使用 DIRS 選項。

DIRS 選項

透過在設定檔中的 TEMPLATES 設定中使用 DIRS 選項,或在 Engine 中使用 dirs 引數,告訴 Django 您的模板目錄在哪裡。這應設定為一個字串清單,其中包含模板目錄的完整路徑。

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

您的模板可以放在您想要的任何位置,只要目錄和模板可由網頁伺服器讀取即可。它們可以有任何您想要的擴充名稱,例如 .html.txt,或者它們可以完全沒有擴充名稱。

請注意,即使在 Windows 上,這些路徑也應使用 Unix 風格的正斜線。

載入器類型

預設情況下,Django 使用基於檔案系統的模板載入器,但 Django 也隨附了一些其他模板載入器,這些載入器知道如何從其他來源載入模板。

其中一些其他載入器預設為停用,但您可以透過將 'loaders' 選項新增至 TEMPLATES 設定中的 DjangoTemplates 後端,或將 loaders 引數傳遞給 Engine 來啟用它們。loaders 應該是一個字串或元組的清單,其中每個代表一個模板載入器類別。以下是 Django 隨附的模板載入器

django.template.loaders.filesystem.Loader

class filesystem.Loader

根據 DIRS 從檔案系統載入模板。

此載入器預設為啟用。但是,除非您將 DIRS 設定為非空的清單,否則它將找不到任何模板。

TEMPLATES = [
    {
        "BACKEND": "django.template.backends.django.DjangoTemplates",
        "DIRS": [BASE_DIR / "templates"],
    }
]

您也可以覆寫 'DIRS',並為特定的檔案系統載入器指定特定的目錄

TEMPLATES = [
    {
        "BACKEND": "django.template.backends.django.DjangoTemplates",
        "OPTIONS": {
            "loaders": [
                (
                    "django.template.loaders.filesystem.Loader",
                    [BASE_DIR / "templates"],
                ),
            ],
        },
    }
]

django.template.loaders.app_directories.Loader

class app_directories.Loader

從檔案系統上的 Django 應用程式載入模板。對於 INSTALLED_APPS 中的每個應用程式,載入器都會尋找 templates 子目錄。如果目錄存在,Django 會在那裡尋找模板。

這表示您可以將模板與您的個別應用程式一起儲存。這也有助於發佈具有預設模板的 Django 應用程式。

例如,對於此設定

INSTALLED_APPS = ["myproject.polls", "myproject.music"]

...然後 get_template('foo.html') 將會按照以下順序在這些目錄中尋找 foo.html

  • /path/to/myproject/polls/templates/

  • /path/to/myproject/music/templates/

... 並會使用它首先找到的那個。

INSTALLED_APPS 的順序非常重要!例如,如果您想要自訂 Django 管理介面,您可能會選擇使用您自己的 myproject.polls 中的 admin/base_site.html 覆寫來自 django.contrib.admin 的標準 admin/base_site.html 模板。然後,您必須確保您的 myproject.pollsINSTALLED_APPS先於 django.contrib.admin,否則 django.contrib.admin 的會先載入,而您的則會被忽略。

請注意,載入器在第一次執行時會執行最佳化:它會快取一份 INSTALLED_APPS 程式套件中哪些具有 templates 子目錄的清單。

您可以將 APP_DIRS 設定為 True 來啟用此載入器

TEMPLATES = [
    {
        "BACKEND": "django.template.backends.django.DjangoTemplates",
        "APP_DIRS": True,
    }
]

django.template.loaders.cached.Loader

class cached.Loader

雖然 Django 模板系統速度相當快,但如果它需要在每次呈現時讀取和編譯您的模板,那麼由此產生的額外負荷可能會加重。

您可以使用它應該包裝的其他載入器清單來配置快取的模板載入器。當第一次遇到未知模板時,會使用包裝的載入器來尋找它們。然後,快取的載入器會將已編譯的 Template 儲存在記憶體中。對於後續載入相同模板的要求,會傳回快取的 Template 執行個體。

如果未指定 OPTIONS['loaders'],則會自動啟用此載入器。

您可以使用類似以下的設定,透過一些自訂的樣板載入器手動指定樣板快取:

TEMPLATES = [
    {
        "BACKEND": "django.template.backends.django.DjangoTemplates",
        "DIRS": [BASE_DIR / "templates"],
        "OPTIONS": {
            "loaders": [
                (
                    "django.template.loaders.cached.Loader",
                    [
                        "django.template.loaders.filesystem.Loader",
                        "django.template.loaders.app_directories.Loader",
                        "path.to.custom.Loader",
                    ],
                ),
            ],
        },
    }
]

注意

所有內建的 Django 樣板標籤都可安全地與快取載入器一起使用,但如果您正在使用來自第三方套件的自訂樣板標籤,或者您自己編寫的標籤,則應確保每個標籤的 Node 實作是執行緒安全的。有關更多資訊,請參閱樣板標籤執行緒安全考量

django.template.loaders.locmem.Loader

class locmem.Loader

從 Python 字典載入樣板。這對於測試很有用。

此載入器會將樣板字典作為其第一個引數

TEMPLATES = [
    {
        "BACKEND": "django.template.backends.django.DjangoTemplates",
        "OPTIONS": {
            "loaders": [
                (
                    "django.template.loaders.locmem.Loader",
                    {
                        "index.html": "content here",
                    },
                ),
            ],
        },
    }
]

此載入器預設為停用。

Django 會根據 'loaders' 選項依序使用樣板載入器。它會使用每個載入器,直到載入器找到符合的項目為止。

自訂載入器

可以使用自訂樣板載入器從其他來源載入樣板。自訂 Loader 類別應繼承自 django.template.loaders.base.Loader,並定義 get_contents()get_template_sources() 方法。

載入器方法

class Loader[原始碼]

從給定的來源(例如檔案系統或資料庫)載入樣板。

get_template_sources(template_name)[原始碼]

一個方法,它接受一個 template_name 並為每個可能的來源產生 Origin 實例。

例如,檔案系統載入器可能會收到 'index.html' 作為 template_name 引數。此方法會為載入器所檢視的每個樣板目錄中出現的 index.html 的完整路徑產生來源。

此方法不需要驗證樣板是否存在於給定的路徑,但應確保路徑有效。例如,檔案系統載入器會確保路徑位於有效的樣板目錄下。

get_contents(origin)

返回給定 Origin 實例的樣板內容。

這是檔案系統載入器從檔案系統讀取內容,或資料庫載入器從資料庫讀取內容的地方。如果不存在相符的樣板,則應引發 TemplateDoesNotExist 錯誤。

get_template(template_name, skip=None)[原始碼]

透過迴圈執行 get_template_sources() 的結果並呼叫 get_contents(),返回給定 template_nameTemplate 物件。這會返回第一個相符的樣板。如果找不到樣板,則會引發 TemplateDoesNotExist

選用的 skip 引數是在擴充樣板時要忽略的來源清單。這允許樣板擴充相同名稱的其他樣板。它也用於避免遞迴錯誤。

一般來說,為自訂樣板載入器定義 get_template_sources()get_contents() 就足夠了。get_template() 通常不需要覆寫。

建構您自己的載入器

如需範例,請閱讀 Django 內建載入器的原始碼

樣板來源

樣板具有一個 origin,其中包含根據載入樣板來源而定的屬性。

class Origin(name, template_name=None, loader=None)[原始碼]
name

樣板載入器返回的樣板路徑。對於從檔案系統讀取的載入器,這是樣板的完整路徑。

如果樣板是直接實例化而不是透過樣板載入器實例化,則此字串值為 <unknown_source>

template_name

傳遞到樣板載入器的樣板相對路徑。

如果樣板是直接實例化而不是透過樣板載入器實例化,則此值為 None

loader

建構此 Origin 的樣板載入器實例。

如果樣板是直接實例化而不是透過樣板載入器實例化,則此值為 None

django.template.loaders.cached.Loader 要求其所有封裝的載入器都設定此屬性,通常是透過使用 loader=self 實例化 Origin 來完成。

返回頂部