模板
作为一个Web框架,Django需要一种动态生成HTML的便捷方法。最常用的方法依赖于模板。模板包含所需HTML输出的静态部分以及描述动态内容将被插入的一些特殊语法。有关创建带有模板的HTML页面的示例,请参阅:示例3
Django项目可以配置一个或多个模板引擎(或者不使用模板)。Django后端内置一个自己的模板系统,创造性地称为Django template language(DTL)和一个流行的替代品JINJA2。后端也可以使用第三方提供其他可用的模板语言。
Django定义了一个标准的API,用于加载和渲染模板,而不用考虑后端的模板系统。加载包括查找给定标识符的模板并对其进行预处理,通常将其编译的结果保存在内存中。渲染工具将上下文数据插入模板并返回结果字符串。
Django 模板语言 是Django自己的模板系统。直到Django 1.8,它是唯一可用的内置选项。这是一个很好的模板库,即使它是相当僵硬和使用时带有它自己特质。如果您没有紧迫的理由需要去选择另一个后端,则应该使用DTL,特别是如果您正在编写可插入的应用程序并打算分发模板。在 Django’s contrib apps 中的有些模板,比如 django.contrib.admin ,使用DTL。
由于历史原因,模板引擎的通用支持和Django模板语言的实现都存在于django.template
模块的命名空间中。
警告
模板系统使用不可信的模板作者的模板是不安全的。例如,一个站点不应该允许它的用户提供他们自己的模板,因为模板作者可以做一些事情,比如执行XSS攻击和拿到包含敏感信息的模板变量的访问权。
1. 模板引擎的支持
1.1 配置
模板引擎在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 包含特定于后端的设置。
1.2 用法
django.template.loader模块定义了两个用于加载模板的函数。
(1)get_template(template_name,using = None)
源码
此函数使用给定名称加载模板并返回一个 Template对象。
返回值的确切类型取决于加载模板的后端。每个后端都有自己的Template类。
get_template()按顺序尝试每个模板引擎,直到成功。如果找不到模板,则会引发模板TemplateDoesNotExist
。如果找到模板但包含无效语法,则会引发该模板 TemplateSyntaxError
。
如何搜索和加载模板取决于每个引擎的后端和配置。
如果要将搜索限制为特定模板引擎,请在using参数中传递引擎NAME。
(2)select_template(template_name_list,using = None)
source
select_template()就像是get_template(),除了它采用模板名称列表。它按顺序尝试每个名称并返回存在的第一个模板。
如果加载模板失败,则django.template可能会引发以下两个异常:
(1)TemplateDoesNotExist(msg,tries = None,backend = None,chain = None)
source
无法找到模板时引发此异常。它接受以下可选参数,用于在调试页面上填充 模板postmortem:
- backend
发生异常的模板后端实例。 - tried
查找模板时尝试的源列表。这被格式化为包含(origin, status)元组的列表,其中origin是origin-like对象,并且status是一个字符串,其中包含未找到模板的原因。 - chain
尝试加载模板时引发的TemplateDoesNotExist
异常列表。这由get_template()尝试从多个引擎加载给定模板的函数使用。
(2)TemplateSyntaxError(msg)
source
找到模板但包含错误时会引发此异常。
(3)Template.render(context = None,request = None)
使用给定的上下文呈现此模板。
通过get_template()和select_template() 返回的Template对象必须提供render()方法。
如果提供context,它必须是字典类型。如果未提供,则引擎将使用空上下文呈现模板。
如果提供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’ engine)
/home/html/default/story_detail.html(‘django’ engine)
/home/html/jinja2/story_detail.html(‘jinja2’ engine)
如果你调用select_template(['story_253_detail.html', 'story_detail.html'])
,这是Django会寻找的:
- /home/html/example.com/story_253_detail.html(‘django’ engine)
/home/html/default/story_253_detail.html(‘django’ engine)
/home/html/jinja2/story_253_detail.html(‘jinja2’ engine)
/home/html/example.com/story_detail.html(‘django’ engine)
/home/html/default/story_detail.html(‘django’ engine)
/home/html/jinja2/story_detail.html(‘jinja2’ engine)
当Django找到存在的模板时,它会停止查找。
小贴士
您可以使用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’ engine)
/home/html/default/news/story_detail.html(‘django’ engine)
/home/html/jinja2/news/story_detail.html(‘jinja2’ engine)
此外,为了减少加载和渲染模板的重复性,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
一个字典用作模板的渲染上下文。 - 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 ,适合从视图返回。
最后,您可以直接使用配置好的引擎:
engines
模板引擎可在 django.template.engines
中使用:
from django.template import engines
django_engine = engines['django']
template = django_engine.from_string("Hello {{ name }}!")
在这个例子中,查找的键“django”是引擎的 NAME。
1.3 内置后端
1.3.1 DjangoTemplates(默认后端)
class DjangoTemplates
源代码
设置BACKEND
为'django.template.backends.django.DjangoTemplates'
,以配置Django模板引擎。
如果APP_DIRS
是True,DjangoTemplates 引擎在应用程序的templates的子目录内寻找模板文件。保留此通用名称是为了向后兼容。
DjangoTemplates引擎接受以下OPTIONS
:
-
'autoescape'
:一个控制是否启用HTML自动转义的布尔值。
它默认为True。
警告 --> 只有在渲染非HTML模板时才设置为False! -
'context_processors'
:用于在使用请求呈现模板时用于填充上下文的可调整的python路径列表。这些可调用对象将请求对象作为其参数,并返回 一个字典而且整合到上下文中。
默认为空列表。有关RequestContext更多信息。 -
'debug'
:一个打开/关闭模板调试模式的布尔值。如果是 True,则错误页面将显示模板呈现期间引发的任何异常的详细报告。此报告包含模板的相关片段,并突出显示相应的行。
它默认为settings.py
文件中DEBUG
设置的值。 -
'loaders'
:模板加载器类的虚拟Python路径列表。每个Loader类都知道如何从特定源导入模板。可选地,可以使用元组而不是字符串。元组中的第一项应该是Loader类名,后续项将Loader在初始化期间传递给它。
*缺省值取决于值DIRS
和APP_DIRS
。*有关详细信息,请参阅加载器类 -
'string_if_invalid'
:作为字符串输出,模板系统应该用于无效(例如拼写错误的)变量。
*默认为空字符串。*有关详细信息,请参见如何处理无效变量 -
'file_charset'
:用于读取磁盘上的模板文件的字符集。
默认值为FILE_CHARSET
。 -
'libraries'
:用于向模板引擎注册的模板标记模块的标签和点缀Python路径的字典。这可用于添加新库或为现有库提供备用标签。例如:
OPTIONS={
'libraries': {
'myapp_tags': 'path.to.myapp.tags',
'admin.urls': 'django.contrib.admin.templatetags.admin_urls',
},
}
可以通过将相应的字典的键传递给{% load %}
标签来加载库。
{% load static %}
'builtins'
:要添加到内置函数的模板标记模块的虚拟Python路径列表。例如:
OPTIONS={
'builtins': ['myapp.builtins'],
}
无需先调用{% load %}
标签即可使用内置库中的标记和过滤器。
1.3.2 Jinja2
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路径列表。这些可调用对象将请求对象作为其参数,并返回 一个字典而且整合到上下文中。
默认为空列表。
不鼓励在Jinja2中使用context_processors。
上下文处理器对Django模板很有用,因为Django模板不支持使用参数调用函数。由于Jinja2没有这个限制,因此建议将使用上下文处理器的函数放在模板可用的全局变量 jinja2.Environment
中,如下所述。然后,您可以在模板中调用该函数:
{{ function(request) }}
一些Django模板上下文处理器返回固定值。对于Jinja2模板,这个间接层不是必需的,因为您可以直接添加常量jinja2.Environment
。
为Jinja2添加context_processors的原始用法包括:
- 根据请求进行耗时的计算。
- 需要每个模板的结果。
- 在每个模板中多次使用结果。
除非满足所有这些条件,否则将函数传递给模板更简单,更符合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的测试。
1.3.3 自定义后端
以下是如何实现自定义模板后端以便使用其他模板系统。一个模板后端是一个继承自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。
1.4 自定义引擎的调试集成
Django调试页面有钩子,可在出现模板错误时提供详细信息。自定义模板引擎可以使用这些挂钩来增强向用户显示的回溯信息。以下钩子可用:
1.4.1 Template postmortem
postmortem会同TemplateDoesNotExist一起出现。它列出了在尝试查找给定模板时使用的模板引擎和加载器。例如,如果配置了两个Django引擎,则postmortem将显示为:
自定义引擎可以通过在引发TemplateDoesNotExist
时传递backend
和tried
参数来填充时间。使用了postmortem的后端应该在模板对象上specify an origin。
1.4.2 行的上下文信息
如果在模板解析或渲染过程中发生错误,Django可以显示错误发生的行。例如:
自定义引擎可以填充这些信息,通过在解析和渲染期间引发的异常上设置template_debug
属性。此属性字典具有以下值:
键的属性 | 含义 |
---|---|
‘name’ | 发生异常的模板的名称。 |
‘message’ | 异常消息 |
‘source_lines’ | 行之前,之后和包括发生异常的行。这是针对上下文的,所以它不应该包含超过20行左右 |
‘line’ | 发生异常的行。 |
‘before’ | 引发错误的token之前的错误行上的内容 |
‘during’ | 引发错误的token |
‘after’ | 引发错误的token后错误行上的内容 |
‘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,
}
1.4.3 Origin API和第三方集成
Django模板提供了一个Origin对象,通过template.origin
属性。这使得调试信息可以显示在模板postmortem中,也可以显示在第三方库中,如Django Debug Toolbar。
自定义引擎可以提供自己的template.origin
信息通过创建指定以下属性的对象:
'name'
:模板的完整路径。'template_name'
:传递到模板加载方法的模板的相对路径。'loader_name'
:标识用于加载模板的函数或类的可选字符串,例如django.template.loaders.filesystem.Loader
。
2.Django 模板语言
2.1 语法
关于本节
这是Django模板语言语法的概述。有关详情请参阅 语言的语法参考。
Django模板只是一个文本文档或使用Django模板语言标记的Python字符串。一些构造由模板引擎识别和解释。主要是变量和标签。
使用上下文呈现模板。渲染将变量替换为其值,这些值在上下文中查找,并执行标记。其他所有内容都按原样输出。
Django模板语言的语法涉及一下四种结构。
2.1.1 变量
变量从上下文输出一个值,这是一个类似于字典的对象,它将键映射到值。
变量被{{}}
包围:
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 }}
如果变量解析为可调用的函数,则模板系统将调用它而不带参数,并使用其结果进行渲染。
译者实例:
(1)在views.py
中定义如下:
class Classval:
def __init__(self):
self.val = 10
self.dicts ={"id":id(self),"val":self.val}
self.lists = ["Classval",self.val]
def getpow(self):
return pow(self.val,3)
def index(request):
cv = Classval()
return render(request,'model_layer/index.html',locals())
(2)在model_layer/index.html
中定义如下html内容:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
cv.val:{{ cv.val }}<hr>
cv.dicts.id:{{ cv.dicts.id }}<hr>
cv.dicts.val:{{ cv.dicts.val }}<hr>
cv.lists.0:{{ cv.lists.0 }}<hr>
cv.lists.1:{{ cv.lists.1 }}<hr>
cv.getpow:{{ cv.getpow }}<hr>
</body>
</html>
(3)输出结果
2.1.2 标签
标签在渲染过程中提供任意逻辑。
这个定义是故意模糊的。例如,标签可以输出内容,用作控制结构,例如“if”语句或“for”循环,从数据库中获取内容,或者甚至允许访问其他模板标签。
标签被{%%}
包围,比如:
{% csrf_token %}
大多数标签接受参数:
{% cycle 'odd' 'even' %}
有些标签需要开始和结束标签:
{% if user.is_authenticated %}Hello, {{ user.username }}.{% endif %}
译者提供了 内置标签 的参考以及编写自定义标签的说明。
2.1.1 过滤器
过滤器转换变量和标签参数的值。
它们看起来像这样:
{{ django|title }}
在上下文中如果有此变量{'django': 'the web framework for perfectionists with deadlines'}
,此模板呈现为:
The Web Framework For Perfectionists With Deadlines
一些过滤器还接收参数:
{{ my_date|date:"Y-m-d" }}
译者提供了内置过滤器 的参考以及 编写自定义过滤器 的说明。
2.1.1 注释
两种注释:单行注释({# ... #}
)和多行注释{% comment %}...{% endcomment %}
。
{# hello 单行注释 #}
{% comment %}
多行注释1
多行注释2
...
{% endcomment %}
2.2 组件
关于本节
这是Django模板语言API的概述。有关详细信息,请参阅API参考。
2.2.1 引擎
django.template.Engine 封装了Django模板系统的一个实例。Engine直接实例化的主要原因 是在Django项目之外使用Django模板语言。
django.template.backends.django.DjangoTemplates是一个适合Django模板后端API 的小型django.template.Engine包装器。
2.2.1 模板
django.template.Template表示已编译的模板。模板是用Engine.get_template()或Engine.from_string()来获得的。
同样,django.template.backends.django.Template是一个适合通用模板API 的小型django.template.Template包装器。
2.2.1 上下文
django.template.Context 除了上下文数据之外还包含一些元数据。它被传递给Template.render()渲染模板。
django.template.RequestContext是Context存储当前 HttpRequest和运行模板上下文处理器的子类 。
通用API没有相同的概念。上下文数据以普通方式传递,如果需要,可以分开传递字典和当前HttpRequest。
2.2.1 加载器
模板加载器负责定位模板,加载模板和返回 Template 对象。
Django提供了 几个内置模板加载器 并支持自定义模板加载器。
2.2.1 上下文处理器
上下文处理器是一个函数,接收当前 HttpRequest为参数并返回一个字典,字典里是要添加到呈现上下文的数据。
它们的主要用途是将所有模板共享的公共数据添加到上下文中,而不必在每个视图中重复代码。
Django提供了许多内置的上下文处理器。实现自定义上下文处理器就像定义函数一样简单。
完