模板¶
作为 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 攻击和访问可能包含敏感信息的模板变量的属性。
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" }}
组件¶
关于本节
这是 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.RequestContext
是 Context
的子类,它存储当前 HttpRequest
并运行模板上下文处理器。
通用 API 没有等效的概念。上下文数据以纯 dict
传递,并且在需要时单独传递当前 HttpRequest
。
上下文处理器¶
上下文处理器是将当前 HttpRequest
作为参数接收并返回要添加到渲染上下文的 dict
数据的函数。
其主要用途是在不重复每个视图中的代码的情况下,向所有模板共享的公共数据添加上下文。
Django 提供了许多 内置上下文处理器,你也可以实现自己的附加上下文处理器。
对模板引擎的支持¶
配置¶
模板引擎使用 TEMPLATES
设置进行配置。它是一个配置列表,每个引擎一个。默认值为空。由 startproject
命令生成的 settings.py
定义了一个更有用的值
TEMPLATES = [
{
"BACKEND": "django.template.backends.django.DjangoTemplates",
"DIRS": [],
"APP_DIRS": True,
"OPTIONS": {
# ... some options here ...
},
},
]
BACKEND
是一个点分 Python 路径,指向实现 Django 模板后端 API 的模板引擎类。内置后端是 django.template.backends.django.DjangoTemplates
和 django.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
是 类似于 origin 的对象,status
是一个字符串,说明找不到模板的原因。 chain
- 尝试加载模板时引发的中间
TemplateDoesNotExist
异常列表。它由函数(例如get_template()
)使用,该函数尝试从多个引擎加载给定模板。
-
exception
TemplateSyntaxError
(msg)¶ 当找到模板但其中包含错误时,会引发此异常。
Template
对象由 get_template()
和 select_template()
返回,它必须提供具有以下签名的 render()
方法
-
Template.
render
(context=None, request=None)¶ 使用给定的上下文呈现此模板。
如果提供了
context
,它必须是一个dict
。如果没有提供,引擎将使用一个空上下文渲染模板。如果提供了
request
,它必须是一个HttpRequest
。然后引擎必须使其以及 CSRF 令牌在模板中可用。如何实现这一点取决于每个后端。
以下是搜索算法的一个示例。对于此示例,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
中。
最后,你可以直接使用已配置的引擎
内置后端¶
-
class
DjangoTemplates
¶
将 BACKEND
设置为 'django.template.backends.django.DjangoTemplates'
以配置 Django 模板引擎。
当 APP_DIRS
为 True
时,DjangoTemplates
引擎在已安装应用程序的 templates
子目录中查找模板。保留此通用名称是为了向后兼容。
DjangoTemplates
引擎接受以下 OPTIONS
'autoescape'
:一个布尔值,用于控制是否启用 HTML 自动转义。其默认值为
True
。警告
仅当您要呈现非 HTML 模板时才将其设置为
False
!'context_processors'
:一个圆点 Python 路径列表,指向在使用请求呈现模板时用于填充上下文的可调用对象。这些可调用对象以请求对象作为其参数,并返回一个dict
,其中包含要合并到上下文中项。默认为空列表。
有关详细信息,请参阅
RequestContext
。'debug'
:一个布尔值,用于打开/关闭模板调试模式。如果为True
,则精美的错误页面将显示模板呈现期间引发的任何异常的详细报告。此报告包含模板的相关片段,并突出显示了相应的行。它默认为
DEBUG
设置的值。'loaders'
:一个点分 Python 路径到模板加载器类的列表。每个Loader
类都知道如何从特定来源导入模板。可以选择使用元组代替字符串。元组中的第一个项目应该是Loader
类名,后续项目在初始化期间传递给Loader
。有关详细信息,请参阅 加载器类型。
'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'
。它是返回 Jinja2 环境的可调用对象的 Python 点状路径。它默认为 'jinja2.Environment'
。Django 调用该可调用对象并将其他选项作为关键字参数传递。此外,Django 为少数选项添加了与 Jinja2 不同的默认值
'autoescape'
:True
'loader'
: 为DIRS
和APP_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
后端会将全局变量 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 支持在模板中将参数传递给可调用对象,因此可以通过在 Jinja2 模板中调用函数来实现许多在 Django 模板中需要模板标签或过滤器的功能,如上例所示。Jinja2 的全局命名空间消除了对模板上下文处理器的需求。Django 模板语言没有与 Jinja2 测试等效的内容。