模板¶
作为一个 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 }}
如果变量解析为可调用对象,则模板系统将不带参数调用它并使用其结果而不是可调用对象。
过滤器¶
过滤器转换变量和标签参数的值。
它们如下所示
{{ django|title }}
在 {'django': 'the web framework for perfectionists with deadlines'}
的上下文中,此模板渲染为
The Web Framework For Perfectionists With Deadlines
某些过滤器采用参数
{{ my_date|date:"Y-m-d" }}
内置过滤器参考 以及 编写自定义过滤器的说明 可供使用。
注释¶
注释如下所示
{# this won't be rendered #}
{% comment %}
标签提供多行注释。
组件¶
关于本节
这是 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
。
加载器¶
模板加载器负责定位模板、加载它们并返回 Template
对象。
上下文处理器¶
上下文处理器是接收当前 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)[source]¶
此函数加载具有给定名称的模板并返回一个
Template
对象。返回值的确切类型取决于加载模板的后端。每个后端都有自己的
Template
类。get_template()
按顺序尝试每个模板引擎,直到其中一个成功。如果找不到模板,它将引发TemplateDoesNotExist
。如果找到模板但包含无效语法,则会引发TemplateSyntaxError
。模板的搜索和加载方式取决于每个引擎的后端和配置。
如果要将搜索限制到特定的模板引擎,请在
using
参数中传递引擎的NAME
。
- select_template(template_name_list, using=None)[source]¶
select_template()
类似于get_template()
,只是它接受一个模板名称列表。它按顺序尝试每个名称,并返回第一个存在的模板。
如果加载模板失败,则可能会引发以下两个异常(在 django.template
中定义)
- exception TemplateDoesNotExist(msg, tried=None, backend=None, chain=None)[source]¶
当找不到模板时,会引发此异常。它接受以下可选参数,用于填充调试页面上的 模板事后分析
backend
引发异常的模板后端实例。
tried
查找模板时尝试过的源列表。它被格式化为包含
(origin, status)
的元组列表,其中origin
是一个 类似源 的对象,而status
是一个字符串,说明找不到模板的原因。chain
尝试加载模板时引发的中间
TemplateDoesNotExist
异常列表。这由诸如get_template()
之类的函数使用,这些函数尝试从多个引擎加载给定模板。
get_template()
和 select_template()
返回的 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)[source]¶
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
,该 HttpResponse
适用于从视图中返回。
最后,您可以直接使用配置的引擎
内置后端¶
将 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 %}
标签,即可使用内置库中的标签和过滤器。
需要安装 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'
。它是一个点分隔的 Python 可调用对象路径,用于返回 Jinja2 环境。默认为 'jinja2.Environment'
。Django 会调用该可调用对象并将其他选项作为关键字参数传递。此外,Django 为一些选项添加了与 Jinja2 不同的默认值
'autoescape'
:True
'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支持在模板中将参数传递给可调用对象,因此许多需要Django模板中的模板标签或过滤器的功能可以通过在Jinja2模板中调用函数来实现,如上例所示。Jinja2的全局命名空间消除了对模板上下文处理器的需求。Django模板语言没有与Jinja2测试等效的功能。