模板 — Django 文档
模板
作为一个 Web 框架,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 攻击和访问可能包含敏感信息的模板变量的属性。
支持模板引擎
配置
模板引擎使用 :setting:`TEMPLATES` 设置进行配置。 这是一个配置列表,每个引擎一个。 默认值为空。 :djadmin:`startproject` 命令生成的 settings.py
定义了一个更有用的值:
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [],
'APP_DIRS': True,
'OPTIONS': {
# ... some options here ...
},
},
]
:设置:`后端 ` 是实现 Django 模板后端 API 的模板引擎类的虚线 Python 路径。 内置后端是 django.template.backends.django.DjangoTemplates 和 django.template.backends.jinja2.Jinja2。
由于大多数引擎从文件加载模板,每个引擎的顶级配置包含两个常见设置:
- :setting:`目录 ` 定义引擎应按搜索顺序查找模板源文件的目录列表。
- :设置:`APP_DIRS ` 告诉引擎是否应该在已安装的应用程序中查找模板。 每个后端都为应用程序内的子目录定义了一个常规名称,其中应存储其模板。
虽然不常见,但可以使用不同的选项配置同一后端的多个实例。 在这种情况下,您应该定义一个唯一的 :setting:`名称 ` 对于每个引擎。
:设置:`选项 ` 包含后端特定的设置。
用法
django.template.loader
模块定义了两个函数来加载模板。
- get_template(template_name, using=None)
此函数加载具有给定名称的模板并返回
Template
对象。返回值的确切类型取决于加载模板的后端。 每个后端都有自己的
Template
类。get_template()
按顺序尝试每个模板引擎,直到成功。 如果找不到模板,它会引发 TemplateDoesNotExist。 如果找到模板但包含无效语法,则会引发 TemplateSyntaxError。如何搜索和加载模板取决于每个引擎的后端和配置。
如果要将搜索限制为特定模板引擎,请传递引擎的 :setting:`名称 ` 在里面
using
争论。
- select_template(template_name_list, using=None)
select_template()
就像get_template()
一样,只是它需要一个模板名称列表。 它按顺序尝试每个名称并返回存在的第一个模板。
如果加载模板失败,则可能会引发 django.template
中定义的以下两个异常:
- exception TemplateDoesNotExist(msg, tried=None, backend=None, chain=None)
- 当找不到模板时会引发此异常。 它接受以下可选参数,用于在调试页面上填充 模板 postmortem:
backend
- 产生异常的模板后端实例。
tried
- 查找模板时尝试的源列表。 这被格式化为包含
(origin, status)
的元组列表,其中origin
是一个 origin-like 对象,而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 令牌在模板中可用。 如何实现取决于每个后端。
这是搜索算法的示例。 对于这个例子,:setting:`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 找到存在的模板时,它会停止查找。
提示
您可以使用 select_template() 进行灵活的模板加载。 例如,如果您编写了一个新闻故事并希望某些故事具有自定义模板,请使用类似 select_template(['story_%s_detail.html' % story.id, 'story_detail.html'])
的内容。 这将允许您为单个故事使用自定义模板,为没有自定义模板的故事使用后备模板。
在包含模板的每个目录内的子目录中组织模板是可能的,而且是更可取的。 约定是为每个 Django 应用程序创建一个子目录,根据需要在这些子目录中包含子目录。
这样做是为了你自己的理智。 将所有模板存储在单个目录的根级别会变得混乱。
要加载子目录中的模板,只需使用斜杠,如下所示:
get_template('news/story_detail.html')
使用与上面相同的 :setting:`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
A
dict
用作模板的渲染上下文。request
一个可选的 HttpRequest,它将在模板的渲染过程中可用。
using
一个可选的模板引擎 :setting:`名称 ` . 模板搜索将仅限于该引擎。
用法示例:
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'
在这个例子中 - 是引擎的 :setting:`名称 ` .
内置后端
- class DjangoTemplates
放 :设置:`后端 ` 到'django.template.backends.django.DjangoTemplates'
配置 Django 模板引擎。
什么时候 :设置:`APP_DIRS ` 是True
,DjangoTemplates
引擎在templates
已安装应用程序的子目录。 保留这个通用名称是为了向后兼容。
DjangoTemplates
引擎接受以下内容 :设置:`选项 ` :
'autoescape'
:控制是否启用 HTML 自动转义的布尔值。默认为
True
。警告
如果您要呈现非 HTML 模板,则仅将其设置为
False
!'context_processors'
:可调用对象的虚线 Python 路径列表,用于在使用请求呈现模板时填充上下文。 这些可调用对象将请求对象作为其参数,并返回要合并到上下文中的项目的dict
。它默认为空列表。
有关更多信息,请参阅 RequestContext。
'debug'
:打开/关闭模板调试模式的布尔值。 如果是True
,花哨的错误页面会显示模板渲染过程中出现的任何异常的详细报告。 此报告包含模板的相关片段,并突出显示了相应的行。它默认为 :setting:`DEBUG` 设置的值。
'loaders'
:模板加载器类的虚线 Python 路径列表。 每个Loader
类都知道如何从特定来源导入模板。 或者,可以使用元组代替字符串。 元组中的第一项应该是Loader
类名,后续项在初始化时传递给Loader
。默认值取决于 :setting:`目录 ` 和 :设置:`APP_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', }, }
可以通过将相应的字典键传递给库来加载库 :ttag:`{% 加载 %} ` 标签。
'builtins'
:要添加到 built-ins 的模板标记模块的虚线 Python 路径列表。 例如:OPTIONS={ 'builtins': ['myapp.builtins'], }
无需首先调用内置库中的标签和过滤器即可使用 :ttag:`{% 加载 %} ` 标签。
- class Jinja2
需要安装 Jinja2:
放 :设置:`后端 ` 到'django.template.backends.jinja2.Jinja2'
配置一个 Jinja2 引擎。
什么时候 :设置:`APP_DIRS ` 是True
,Jinja2
引擎在jinja2
已安装应用程序的子目录。
最重要的入口 :设置:`选项 ` 是'environment'
. 它是返回 Jinja2 环境的可调用对象的虚线 Python 路径。 默认为 'jinja2.Environment'
。 Django 调用该 callable 并将其他选项作为关键字参数传递。 此外,Django 为一些选项添加了与 Jinja2 不同的默认值:
'autoescape'
:True
'loader'
: 一个加载器配置为 :setting:`目录 ` 和 :设置:`APP_DIRS `'auto_reload'
:settings.DEBUG
'undefined'
:DebugUndefined if settings.DEBUG else Undefined
Jinja2
引擎也接受以下内容 :设置:`选项 ` :
'context_processors'
:可调用对象的虚线 Python 路径列表,用于在使用请求呈现模板时填充上下文。 这些可调用对象将请求对象作为其参数,并返回要合并到上下文中的项目的dict
。它默认为空列表。
不鼓励在 Jinja2 模板中使用上下文处理器。
上下文处理器对 Django 模板很有用,因为 Django 模板不支持使用参数调用函数。 由于 Jinja2 没有这个限制,建议使用
jinja2.Environment
将用作上下文处理器的函数放在模板可用的全局变量中,如下所述。 然后您可以在模板中调用该函数:{{ function(request) }}
一些 Django 模板上下文处理器返回一个固定值。 对于 Jinja2 模板,这个间接层不是必需的,因为您可以直接在
jinja2.Environment
中添加常量。为 Jinja2 添加上下文处理器的原始用例涉及:
根据请求进行昂贵的计算。
在每个模板中都需要结果。
在每个模板中多次使用结果。
除非所有这些条件都满足,否则向模板传递一个函数更简单,更符合Jinja2的设计。
默认配置有意保持在最低限度。 如果模板随请求一起呈现(例如 当使用 render()) 时,Jinja2
后端将全局变量 request
、csrf_input
和 csrf_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 测试等效的语言。
自定义后端
以下是如何实现自定义模板后端以使用另一个模板系统。 模板后端是一个继承 django.template.backends.base.BaseEngine
的类。 它必须实现 get_template()
和可选的 from_string()
。 下面是一个虚构的 foobar
模板库的示例:
from django.template import TemplateDoesNotExist, TemplateSyntaxError
from django.template.backends.base import BaseEngine
from django.template.backends.utils import csrf_input_lazy, csrf_token_lazy
import foobar
class FooBar(BaseEngine):
# Name of the subdirectory containing the templates for this engine
# inside an installed application.
app_dirname = 'foobar'
def __init__(self, params):
params = params.copy()
options = params.pop('OPTIONS').copy()
super().__init__(params)
self.engine = foobar.Engine(**options)
def from_string(self, template_code):
try:
return Template(self.engine.from_string(template_code))
except foobar.TemplateCompilationFailed as exc:
raise TemplateSyntaxError(exc.args)
def get_template(self, template_name):
try:
return Template(self.engine.get_template(template_name))
except foobar.TemplateNotFound as exc:
raise TemplateDoesNotExist(exc.args, backend=self)
except foobar.TemplateCompilationFailed as exc:
raise TemplateSyntaxError(exc.args)
class Template:
def __init__(self, template):
self.template = template
def render(self, context=None, request=None):
if context is None:
context = {}
if request is not None:
context['request'] = request
context['csrf_input'] = csrf_input_lazy(request)
context['csrf_token'] = csrf_token_lazy(request)
return self.template.render(context)
有关详细信息,请参阅 DEP 182。
自定义引擎的调试集成
Django 调试页面具有钩子,可在出现模板错误时提供详细信息。 自定义模板引擎可以使用这些钩子来增强向用户显示的回溯信息。 以下挂钩可用:
模板事后分析
当提出 TemplateDoesNotExist 时,会出现事后分析。 它列出了在尝试查找给定模板时使用的模板引擎和加载器。 例如,如果配置了两个 Django 引擎,事后分析将显示为:
../_images/postmortem.png
自定义引擎可以通过在引发 TemplateDoesNotExist 时传递 backend
和 tried
参数来填充事后分析。 使用事后分析 的后端应在模板对象上指定原点 。
上下文线路信息
如果在模板解析或渲染过程中发生错误,Django 可以显示发生错误的行。 例如:
../_images/template-lines.png
自定义引擎可以通过在解析和呈现期间引发的异常设置 template_debug
属性来填充此信息。 该属性是一个 dict
,具有以下值:
'name'
:发生异常的模板名称。'message'
:异常信息。'source_lines'
:发生异常的行之前、之后和包括行。 这是用于上下文的,因此它不应包含超过 20 行左右。'line'
:发生异常的行号。'before'
:在引发错误的标记之前的错误行上的内容。'during'
:引发错误的令牌。'after'
:在引发错误的标记之后的错误行上的内容。'total'
:source_lines
中的行数。'top'
:source_lines
开始的行号。'bottom'
:source_lines
结束的行号。
鉴于上述模板错误,template_debug
将如下所示:
{
'name': '/path/to/template.html',
'message': "Invalid block tag: 'syntax'",
'source_lines': [
(1, 'some\n'),
(2, 'lines\n'),
(3, 'before\n'),
(4, 'Hello {% syntax error %} {{ world }}\n'),
(5, 'some\n'),
(6, 'lines\n'),
(7, 'after\n'),
(8, ''),
],
'line': 4,
'before': 'Hello ',
'during': '{% syntax error %}',
'after': ' {{ world }}\n',
'total': 9,
'bottom': 9,
'top': 1,
}
Origin API 和第三方集成
Django 模板有一个 Origin 对象,可通过 template.origin
属性使用。 这使调试信息能够显示在 模板 postmortem 中,以及在 3rd-party 库中,如 Django 调试工具栏 。
自定义引擎可以通过创建指定以下属性的对象来提供自己的 template.origin
信息:
'name'
:模板的完整路径。'template_name'
:传递给模板加载方法的模板的相对路径。'loader_name'
:标识用于加载模板的函数或类的可选字符串,例如django.template.loaders.filesystem.Loader
。
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 }}
如果变量解析为可调用对象,模板系统将不带参数调用它,并使用其结果而不是可调用对象。
组件
发动机
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.RequestContext 是 Context 的子类,它存储当前的 HttpRequest 并运行模板上下文处理器。
通用 API 没有等效的概念。 上下文数据在一个普通的 dict
中传递,如果需要,当前的 HttpRequest 将单独传递。
上下文处理器
上下文处理器是接收当前 HttpRequest 作为参数并返回要添加到渲染上下文的数据 dict
的函数。
它们的主要用途是将所有模板共享的公共数据添加到上下文中,而无需在每个视图中重复代码。
Django 提供了许多 内置上下文处理器 。 实现自定义上下文处理器就像定义一个函数一样简单。
评论
评论看起来像这样:
一种 :ttag:`{% 评论 %} ` 标签提供多行注释。