Django 模板语言:面向 Python 程序员

本文档从技术角度解释了 Django 模板系统——它的工作原理以及如何扩展它。如果您正在查找有关语言语法的参考,请参阅Django 模板语言

本指南假设您已经了解模板、上下文、变量、标签和渲染。如果您不熟悉这些概念,请先阅读Django 模板语言简介

概述

在 Python 中使用模板系统是一个三步过程

  1. 您配置一个Engine

  2. 您将模板代码编译成一个Template

  3. 您使用Context渲染模板。

Django 项目通常依赖于高级、与后端无关的 API来完成这些步骤中的每一个步骤,而不是模板系统的低级 API。

  1. 对于DjangoTemplates设置中的每个TEMPLATES后端,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)[source]

实例化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'

    • 'django.template.loaders.app_directories.Loader'当且仅当app_dirsTrue时。

    然后,这些加载器将被包装在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()[source]

返回第一个已配置的DjangoTemplates引擎中的底层Engine。如果未配置任何引擎,则引发ImproperlyConfigured

这对于保留依赖于全局可用、隐式配置引擎的 API 是必需的。强烈建议不要进行任何其他用途。

Engine.from_string(template_code)[source]

编译给定的模板代码并返回一个Template对象。

Engine.get_template(template_name)[source]

加载具有给定名称的模板,对其进行编译并返回一个Template对象。

Engine.select_template(template_name_list)[source]

类似于get_template(),只不过它接受名称列表并返回找到的第一个模板。

加载模板

创建Template的推荐方法是调用Engine的工厂方法:get_template()select_template()from_string()

TEMPLATES设置定义了DjangoTemplates引擎的 Django 项目中,可以直接实例化Template。如果定义了多个DjangoTemplates引擎,则将使用第一个引擎。

class Template[source]

此类位于django.template.Template。构造函数接受一个参数——原始模板代码。

from django.template import Template

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

幕后

系统仅在创建Template对象时才解析一次原始模板代码。从那时起,它以树结构的形式存储在内部以提高性能。

即使是解析本身也相当快。大部分解析都是通过对单个简短正则表达式的单次调用完成的。

渲染上下文

拥有已编译的Template对象后,可以使用它来渲染上下文。您可以重用相同的模板来使用不同的上下文渲染它多次。

class Context(dict_=None, autoescape=True, use_l10n=None, use_tz=None)[source]

django.template.Context的构造函数接受一个可选参数——一个字典,用于将变量名映射到变量值。

还可以指定三个可选关键字参数。

  • autoescape 控制是否启用 HTML 自动转义。

    它默认为True

    警告

    只有在渲染非 HTML 模板时才将其设置为False

  • use_l10n覆盖默认情况下是否本地化值。如果设置为True,则数字和日期将根据区域设置进行格式化。

    它默认为None

    有关详细信息,请参阅控制模板中的本地化

  • use_tz覆盖在模板中渲染时是否将日期转换为本地时间。如果设置为True,则所有日期都将使用本地时区进行渲染。这优先于USE_TZ

    它默认为None

    有关详细信息,请参阅模板中的时区感知输出

有关使用方法示例,请参见下面的使用 Context 对象

Template.render(context)[source]

使用Context调用Template对象的render()方法来“填充”模板。

>>> 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]

请注意,如果模板表达式(如{{ 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
    
  • 有时,您可能出于其他原因想要关闭此功能,并告诉模板系统无论如何都不要调用变量。为此,请在可调用对象上设置一个值为 Truedo_not_call_in_templates 属性。然后,模板系统将表现得好像您的变量不可调用一样(例如,允许您访问可调用对象的属性)。

无效变量的处理方式

通常,如果变量不存在,模板系统将插入引擎的 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[source]

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)[source]

除了 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)[source]

Django 自带一个特殊的 Context 类,django.template.RequestContext,它的行为与普通的 django.template.Context 略有不同。第一个区别在于它接受一个 HttpRequest 对象作为它的第一个参数。例如:

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

第二个区别在于它会根据引擎的 context_processors 配置选项自动填充上下文变量。

context_processors 选项是一个可调用对象的列表——称为**上下文处理器**——它们以请求对象作为参数,并返回一个字典,字典中的项目将被合并到上下文中。在默认生成的 settings 文件中,默认模板引擎包含以下上下文处理器:

[
    "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'。这是一个与安全相关的上下文处理器,管理员和其他 contrib 应用都需要它;为避免意外的错误配置,它被故意硬编码,无法在 context_processors 选项中关闭。

每个处理器按顺序应用。这意味着,如果一个处理器向上下文中添加了一个变量,而第二个处理器又添加了一个同名变量,则第二个处理器将覆盖第一个处理器。

应用上下文处理器时:

上下文处理器应用于上下文数据的顶部。这意味着上下文处理器可能会覆盖您提供给 ContextRequestContext 的变量,因此请注意避免与上下文处理器提供的变量名称重叠。

如果希望上下文数据优先于上下文处理器,请使用以下模式:

from django.template import RequestContext

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

Django 这样做是为了允许上下文数据在诸如 render()TemplateResponse 等 API 中覆盖上下文处理器。

此外,您可以使用可选的第三个位置参数 processorsRequestContext 提供附加处理器列表。在此示例中,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))

内置模板上下文处理器

以下是每个内置处理器的功能:

django.contrib.auth.context_processors.auth

auth(request)[source]

如果启用此处理器,则每个 RequestContext 都将包含以下变量:

  • user – 代表当前登录用户的 auth.User 实例(如果客户端未登录,则为 AnonymousUser 实例)。

  • permsdjango.contrib.auth.context_processors.PermWrapper 的一个实例,表示当前登录用户拥有的权限。

django.template.context_processors.debug

debug(request)[source]

如果启用此处理器,则每个 RequestContext 都将包含这两个变量——但仅当您的 DEBUG 设置设置为 True 且请求的 IP 地址 (request.META['REMOTE_ADDR']) 位于 INTERNAL_IPS 设置中时。

  • debugTrue。您可以在模板中使用它来测试您是否处于 DEBUG 模式。

  • sql_queries – 一个 {'sql': ..., 'time': ...} 字典列表,表示在请求期间发生的每个 SQL 查询以及花费的时间。列表按数据库别名,然后按查询顺序排列。它是在访问时惰性生成的。

django.template.context_processors.i18n

i18n(request)[source]

如果启用此处理器,则每个 RequestContext 都将包含以下变量:

  • LANGUAGESLANGUAGES 设置的值。

  • LANGUAGE_BIDI – 如果当前语言是右到左语言(例如希伯来语、阿拉伯语),则为 True;如果是左到右语言(例如英语、法语、德语),则为 False

  • LANGUAGE_CODE – 如果存在,则为 request.LANGUAGE_CODE;否则,为 LANGUAGE_CODE 设置的值。

有关生成相同值的模板标签,请参见 i18n 模板标签

django.template.context_processors.media

如果启用此处理器,则每个 RequestContext 都将包含一个变量 MEDIA_URL,提供 MEDIA_URL 设置的值。

django.template.context_processors.static

static(request)[source]

如果启用此处理器,则每个 RequestContext 都将包含一个变量 STATIC_URL,提供 STATIC_URL 设置的值。

django.template.context_processors.csrf

此处理器添加一个令牌,该令牌由 csrf_token 模板标签用于防止 跨站请求伪造

django.template.context_processors.request

如果启用此处理器,则每个 RequestContext 都将包含一个变量 request,它是当前的 HttpRequest

django.template.context_processors.tz

tz(request)[source]

如果启用此处理器,每个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'选项(或者如果您直接使用它,则使用Enginecontext_processors参数)指向。

加载模板

通常,您会将模板存储在文件系统上的文件中,而不是自己使用低级的Template API。将模板保存在指定为**模板目录**的目录中。

Django 在许多位置搜索模板目录,具体取决于您的模板加载设置(请参见下面的“加载器类型”),但指定模板目录最基本的方法是使用DIRS选项。

DIRS选项

通过在设置文件中的TEMPLATES设置中使用DIRS选项(或Enginedirs参数)来告诉 Django 您的模板目录是什么。这应该设置为包含模板目录完整路径的字符串列表。

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

您的模板可以放在任何您想要的地方,只要 Web 服务器可以读取这些目录和模板即可。它们可以使用任何您想要的扩展名,例如.html.txt,或者根本没有扩展名。

请注意,即使在 Windows 上,这些路径也应使用 Unix 风格的正斜杠。

加载器类型

默认情况下,Django 使用基于文件系统的模板加载器,但 Django 还附带了一些其他模板加载器,它们知道如何从其他来源加载模板。

其中一些其他加载器默认情况下是禁用的,但是您可以通过在TEMPLATES设置中的DjangoTemplates后端添加'loaders'选项或将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 管理界面,您可以选择使用您自己的admin/base_site.html(位于myproject.polls中)来覆盖来自django.contrib.admin的标准admin/base_site.html模板。然后,您必须确保您的myproject.polls位于INSTALLED_APPSdjango.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[source]

从给定的源加载模板,例如文件系统或数据库。

get_template_sources(template_name)[source]

此方法接受一个template_name并为每个可能的源生成Origin实例。

例如,文件系统加载器可能会收到'index.html'作为template_name参数。此方法将为index.html在加载器查看的每个模板目录中出现的完整路径生成源。

此方法不需要验证模板是否存在于给定路径,但应确保路径有效。例如,文件系统加载器确保路径位于有效的模板目录下。

get_contents(origin)

给定一个Origin实例,返回模板的内容。

这就是文件系统加载器从文件系统读取内容的地方,或者数据库加载器从数据库读取内容的地方。如果找不到匹配的模板,则应引发TemplateDoesNotExist错误。

get_template(template_name, skip=None)[source]

通过循环遍历get_template_sources()的结果并调用get_contents(),为给定的template_name返回一个Template对象。这将返回第一个匹配的模板。如果找不到模板,则会引发TemplateDoesNotExist

可选的skip参数是一个要忽略的源列表,用于扩展模板。这允许模板扩展相同名称的其他模板。它还用于避免递归错误。

通常,为自定义模板加载器定义get_template_sources()get_contents()就足够了。get_template()通常不需要重写。

构建您自己的

例如,请阅读Django 内置加载器的源代码

模板源

模板具有一个origin,其中包含根据其加载源而不同的属性。

class Origin(name, template_name=None, loader=None)[source]
name

模板加载器返回的模板路径。对于从文件系统读取的加载器,这是模板的完整路径。

如果模板是直接实例化的,而不是通过模板加载器实例化的,则这是一个<unknown_source>的字符串值。

template_name

传递到模板加载器的模板的相对路径。

如果模板是直接实例化的,而不是通过模板加载器实例化的,则为None

loader

构造此Origin的模板加载器实例。

如果模板是直接实例化的,而不是通过模板加载器实例化的,则为None

django.template.loaders.cached.Loader要求其所有包装的加载器都设置此属性,通常通过使用loader=self实例化Origin来实现。

返回顶部