Django 模板语言:对于 Python 开发者¶
这篇文档从技术角度解释了 Django 模板系统——它是如何工作的以及如何扩展它。如果你想找语言语法的参考,请看 Django 模板语言。
它的前提是对模板、上下文、变量、标签和渲染的理解。如果你不熟悉这些概念,可以从 Django 模板语言介绍 开始。
概况¶
在 Python 中使用模板系统是一个三步走的过程:
Django 项目一般依靠 高级、后端不可知的 API 来完成每一个步骤,而不是模板系统的低级 API。
对于
TEMPLATES
设置中的每一个DjangoTemplates
后端,Django 都会实例化一个Engine
。DjangoTemplates
封装Engine
并将其适配到通用的模板后端 API 中。django.template.loader
模块提供了get_template()
等函数来加载模板。它们返回一个django.template.backends.django.Template
,它封装了实际的django.template
。上一步得到的
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
。Warning
只有当你渲染非 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_dirs
为True
时。
然后,这些加载器会被包装在
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.select_template(template_name_list)[source]¶
就像
get_template()
一样,只不过它接收一个名称列表,并返回找到的第一个模板。
加载模板¶
推荐的创建 Template
的方法是调用 Engine
的工厂方法: get_template()
、 select_template()
和 from_string()
。
在 Django 项目中,如果 TEMPLATES
设置定义了一个 DjangoTemplates
引擎,那么可以直接实例化一个 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
的构造函数需要一个可选的参数——一个将变量名映射到变量值的字典。Three optional keyword arguments can also be specified:
autoescape
控制是否启用 HTML 自动转码。默认为
True
。Warning
只有当你渲染非 HTML 模板时,才将其设置为
False
!use_l10n
overrides whether values will be localized by default. If set toTrue
numbers and dates will be formatted based on locale.It defaults to
None
.See 在模板中控制本地化 for details.
use_tz
overrides whether dates are converted to the local time when rendered in a template. If set toTrue
all dates will be rendered using the local timezone. This takes precedence overUSE_TZ
.It defaults to
None
.See 模板中有时区的输出 for details.
For example usage, see 使用 Context 对象 below.
- 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
,否则异常将被传播。如果异常确实具有值为True
的silent_variable_failure
属性,则变量将呈现为引擎的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 数据库 APIDoesNotExist
异常的基类,有silent_variable_failure = True
。所以如果你使用 Django 模板与 Django 模型对象,任何DoesNotExist
异常都会静默失败。一个变量只有在没有所需参数的情况下才可以被调用,否则,系统将返回引擎的
string_if_invalid
选项的值。否则,系统将返回引擎的string_if_invalid
选项的值。
在调用一些变量的时候可能会有副作用,如果让模板系统访问这些变量,要么是傻瓜,要么是安全漏洞。
一个很好的例子是每个 Django 模型对象上的
delete()
方法。模板系统不应该允许执行这样的操作:I will now delete this valuable data. {{ data.delete }}
为了防止这种情况发生,在可调用的变量上设置一个
alters_data
属性。如果设置了alters_data=True
,模板系统将不会调用变量,而是无条件地用string_if_invalid
替换变量。 动态生成的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
被设置为任何其他值,变量过滤器将被忽略。
对于 if
、for
和 regroup
模板标签,这种行为略有不同。如果向这些模板标签之一提供了一个无效的变量,该变量将被解释为 None
。过滤器总是应用于这些模板标签中的无效变量。
如果 string_if_invalid
包含 '%s'
,格式标记将被替换为无效变量的名称。
仅供调试使用!
虽然 string_if_invalid
是一个有用的调试工具,但把它作为“开发默认值”是一个坏主意。
很多模板,包括一些 Django 的模板,在遇到不存在的变量时,都会依靠模板系统的静默。如果你给 ''
string_if_invalid
以外的值,你会在这些模板和网站上遇到渲染问题。
一般来说,string_if_invalid
只有在调试某个特定的模板问题时才应该启用,调试完成后再清除。
内置变量¶
每个上下文都包含 True
、False
和 None
。正如你所期望的那样,这些变量解析为相应的 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
在上下文中,则返回其值。否则用default
值插入key
并返回default
。
- Context.pop()¶
- Context.push()¶
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'
除了 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()
的结果在单元测试中可以用来比较 Context
和 dict
:
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 comes with a special Context
class,
django.template.RequestContext
, that acts slightly differently from the
normal django.template.Context
. The first difference is that it takes an
HttpRequest
as its first argument. For example:
c = RequestContext(
request,
{
"foo": "bar",
},
)
第二个区别是,它根据引擎的 context_processors
配置选项,自动给上下文填充一些变量。
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'
。 这是管理和其他 contrib 应用所需要的安全相关的上下文处理器,为了防止意外的错误配置,特意将其硬编码进去,不能在 context_processors
选项中关闭。
每个处理器都是按顺序应用的。这意味着,如果一个处理器向上下文添加了一个变量,而第二个处理器添加了一个同名的变量,第二个处理器将覆盖第一个处理器。下面解释默认的处理器。
当应用上下文处理器时
上下文处理器是应用在上下文数据之上的。这意味着上下文处理器可能会覆盖你提供给 Context
或 RequestContext
的变量,所以要注意避免变量名与上下文处理器提供的变量名重叠。
如果你想让上下文数据优先于上下文处理器,请使用以下模式:
from django.template import RequestContext
request_context = RequestContext(request)
request_context.push({"my_name": "Adrian"})
Django 这样做是为了让上下文数据覆盖 API 中的上下文处理器,如 render()
和 TemplateResponse
。
此外,你还可以使用可选的第三个位置参数 processors
,给 RequestContext
一个额外的处理器列表。在这个例子中, 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
¶
如果启用了这个处理器,每一个 RequestContext
都会包含这些变量:
user
—— 代表当前登录用户的auth.User
实例(如果客户端没有登录,则为AnonymousUser
实例)。perms
——django.contrib.uth.context_processors.PermWrapper
的实例,表示当前登录用户拥有的权限。
django.template.context_processors.debug
¶
如果启用了这个处理器,每一个 RequestContext
都会包含这两个变量——但前提是你的 DEBUG
设置为 True
,并且请求的 IP 地址(request.META['REMOTE_ADDR']
)在 INTERNAL_IPS
配置中:
debug
——True
。你可以在模板中使用它来测试你是否处于DEBUG
模式。sql_queries
——{'sql': ..., 'time': ...}
字典的列表,表示在请求过程中迄今为止发生的每一个 SQL 查询,以及花费的时间。这个列表是按照数据库别名,然后按照查询的顺序排列的。它是在访问时惰性生成的。
django.template.context_processors.i18n
¶
如果启用了这个处理器,每一个 RequestContext
都会包含这些变量:
LANGUAGES
——LANGUAGES
配置值。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
¶
如果启用了这个处理器,每个 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
¶
如果启用了这个处理器,每个 RequestContext
将包含一个变量 TIME_ZONE
,提供当前活动时区的名称。
django.contrib.messages.context_processors.messages
¶
如果启用了这个处理器,每一个 RequestContext
都会包含这两个变量:
编写你自己的上下文处理器¶
上下文处理器具有简单的接口:它是一个 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。将模板保存在指定的 template 目录 中。
Django 会在很多地方搜索模板目录,这取决于你的模板加载设置(见下面的“加载器类型”),但最基本的指定模板目录的方法是使用 DIRS
选项。
DIRS
选项¶
通过使用配置文件中的 TEMPLATES
配置中的 DIRS
选项来告诉 Django 你的模板目录是什么,或者使用 Engine
中的 dirs
参数。这应该被设置为一个字符串列表,其中包含你的模板目录的完整路径:
TEMPLATES = [
{
"BACKEND": "django.template.backends.django.DjangoTemplates",
"DIRS": [
"/home/html/templates/lawrence.com",
"/home/html/templates/default",
],
},
]
您的模板可以放在任何地方,只要目录和模板对于 Web 服务器是可读的。它们可以具有任何您想要的扩展名,例如 .html
或 .txt
,或者甚至可以没有扩展名。
请注意,这些路径应该使用 Unix 风格的斜线,即使在 Windows 上也是如此。
加载器类型¶
默认情况下,Django 使用的是基于文件系统的模板加载器,但 Django 自带了一些其他的模板加载器,它们知道如何从其他来源加载模板。
其他一些加载器默认是禁用的,但是你可以通过在 TEMPLATES
配置中为你的 DjangoTemplates
后端添加一个 'loaders'
选项来激活它们,或者向 Engine
传递一个 loaders
参数。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 管理,你可能会选择覆盖标准django.contrib.admin
的admin/base_site.html
模板,,用你自己myproject.polls
中的admin/base_site.html
。然后你必须确保你的myproject.polls
在INSTALLED_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", ], ), ], }, } ]
Note
所有内置的 Django 模板标签都可以安全地使用缓存加载器,但如果你使用的是来自第三方包的自定义模板标签,或者你自己编写的模板标签,你应该确保每个标签的“节点”实现是线程安全的。更多信息,请参见 模板标签线程安全注意事项。
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
。