Django4 中文入门教程 Django4.0 模板-模板引擎的支持

2024-02-25 开发教程 Django4 中文入门教程 匿名 5

配置

模板引擎是通过 ​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 发现一个模板存在时,它就会停止寻找。

提示:你可以使用 ​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 ​,适合从视图返回。
最后,您可以直接使用配置好的引擎:

engines

模板引擎可在 ​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​。只有当你渲染非 HTML 模板时,才将其设置为 ​False​!

context_processors​:当模板被请求渲染时,用于填充上下文的可调用项的点分隔 Python 路径列表。这些可调用的对象以一个请求对象作为参数,并返回一个 ​dict ​的项目,这些项目将被合并到上下文中。默认为空列表。

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 %}​ 标签。

class Jinja2

需要安装 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
  • 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 支持在模板中向可调用对象传递参数,所以很多在 Django 模板中需要模板标签或过滤器的功能都可以通过在 Jinja2 模板中调用函数来实现,如上例所示。Jinja2 的全局命名空间消除了对模板上下文处理器的需求。Django 模板语言并没有与 Jinja2 测试相对应的功能。