模板
作为一个网络框架,Django 需要一种方便的方式来动态生成 HTML。最常见的方法是依靠模板。一个模板包含了所需 HTML 输出的静态部分,以及一些特殊的语法,描述了如何插入动态内容。
Django 定义了一个标准的API,用于加载和渲染模板,而不考虑后端。加载包括为给定的标识符找到模板并对其进行预处理,通常是将其编译成内存中的表示形式。渲染是指将上下文数据插入模板,并返回结果字符串。
语法
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 %}
可参考dijango提供 内置标签参考 以及 自定义标签编写指南。
过滤器
过滤器转换变量和标签参数的值。
它们看起来像这样:
{{ django|title }}
使用上下文给定的键值对 {'django': 'the web framework for perfectionists with deadlines'}
,这个模板渲染为:
The Web Framework For Perfectionists With Deadlines
一些过滤器接受一个参数:
{{ my_date|date:"Y-m-d" }}
可参考django提供 内建过滤器参考 以及 自定义过滤器编写指南.
注释
注释看起来像这样:
{# this won't be rendered #}
{% comment %} 标签提供多行注释
组件
引擎
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 没有对应的概念。上下文数据以普通的字典传递,而当前的 HttpRequest 则根据需要单独传递。
加载器
模板加载器负责定位模板,加载模板,并返回 Template 对象。
Django 提供了几个 内建模板加载器 并且支持 自定义模板加载器。
默认情况下,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"],
}
]
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,
}
]
上下文处理器
上下文处理器是接收当前的 HttpRequest 作为参数,并返回一个 dict 的数据添加到渲染上下文的函数。
它们的主要用途是将所有模板共享的通用数据添加到上下文中,而无需在每个视图中重复代码。
Django 提供了许多 内置上下文处理器,你也可以实现自己的其他上下文处理器。
模板引擎的支持
配置
模板引擎是通过 TEMPLATES 进行配置。这是一个配置列表,每个引擎都有一个。默认值为空。startproject 命令生成的 settings.py 定义了一个更有用的值:
TEMPLATES = [
{
"BACKEND": "django.template.backends.django.DjangoTemplates",
"DIRS": [],
"APP_DIRS": True,
"OPTIONS": {
# ... some options here ...
},
},
]
BACKEND 是实现 Django 模板后端 API 的模板引擎类的点分隔 Python 路径。内置的后端有 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)
当找到模板但包含错误时,将引发此异常。
由 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)
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 ,适合从视图返回。
最后,您可以直接使用配置好的引擎:
引擎
模板引擎可在 django.template.engines 中使用:
from django.template import engines
django_engine = engines["django"]
template = django_engine.from_string("Hello {{ name }}!")
在这个例子中,查找关键字“django”是引擎的 NAME。
内置后端
class DjangoTemplates
设置 BACKEND 为 ‘django.template.backends.django.DjangoTemplates’,以配置 Django 模板引擎。
当 APP_DIRS 为 True 时,DjangoTemplates 引擎会在已安装应用程序的 templates 子目录中寻找模板。这个通用名称是为了向后兼容而保留的。
DjangoTemplates 引擎接受以下 OPTIONS:
‘autoescape’:一个布尔值,控制是否启用 HTML 自动转义。
默认为 True。
‘context_processors’:当模板被请求渲染时,用于填充上下文的可调用项的点分隔 Python 路径列表。这些可调用的对象以一个请求对象作为参数,并返回一个 dict 的项目,这些项目将被合并到上下文中。
默认为空列表。
查看 RequestContext 获取更多信息。
‘debug’:开启/关闭模板调试模式的布尔值。如果是 True,错误页面将显示模板渲染过程中出现的任何异常的详细报告。该报告包含模板的相关片段,并突出显示相应的行。
默认为 DEBUG 配置的值。
‘loaders’:模板加载器类的点分隔 Python 路径列表。每个 Loader 类都知道如何从特定源导入模板。可以选择使用元组来代替字符串。元组中的第一项应该是 Loader 类名,随后的项在初始化期间传递给 Loader。
默认值取决于 DIRS 和 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",
},
}
可以通过将相应的字典键传递到 {% load %} 标签来加载库。
‘builtins’:要添加的 内置模板标签和过滤器 的点分隔 Python 路径列表。例如:
OPTIONS = {
"builtins": ["myapp.builtins"],
}
可以使用内置库中的标签和过滤器,而不需要先调用 {% load %} 标签。