模板 — Django 文档

来自菜鸟教程
Django/docs/2.2.x/topics/templates
跳转至:导航、​搜索

模板

作为一个 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 攻击和访问可能包含敏感信息的模板变量的属性。


支持模板引擎

配置

模板引擎使用 :setting:`TEMPLATES` 设置进行配置。 这是一个配置列表,每个引擎一个。 默认值为空。 :djadmin:`startproject` 命令生成的 settings.py 定义了一个更有用的值:

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [],
        'APP_DIRS': True,
        'OPTIONS': {
            # ... some options here ...
        },
    },
]

:设置:`后端 ` 是实现 Django 模板后端 API 的模板引擎类的虚线 Python 路径。 内置后端是 django.template.backends.django.DjangoTemplates 和 django.template.backends.jinja2.Jinja2。

由于大多数引擎从文件加载模板,每个引擎的顶级配置包含两个常见设置:

  • :setting:`目录 ` 定义引擎应按搜索顺序查找模板源文件的目录列表。
  • :设置:`APP_DIRS ` 告诉引擎是否应该在已安装的应用程序中查找模板。 每个后端都为应用程序内的子目录定义了一个常规名称,其中应存储其模板。

虽然不常见,但可以使用不同的选项配置同一后端的多个实例。 在这种情况下,您应该定义一个唯一的 :setting:`名称 ` 对于每个引擎。

:设置:`选项 ` 包含后端特定的设置。


用法

django.template.loader 模块定义了两个函数来加载模板。

get_template(template_name, using=None)

此函数加载具有给定名称的模板并返回 Template 对象。

返回值的确切类型取决于加载模板的后端。 每个后端都有自己的 Template 类。

get_template() 按顺序尝试每个模板引擎,直到成功。 如果找不到模板,它会引发 TemplateDoesNotExist。 如果找到模板但包含无效语法,则会引发 TemplateSyntaxError。

如何搜索和加载模板取决于每个引擎的后端和配置。

如果要将搜索限制为特定模板引擎,请传递引擎的 :setting:`名称 ` 在里面using争论。

select_template(template_name_list, using=None)
select_template() 就像 get_template() 一样,只是它需要一个模板名称列表。 它按顺序尝试每个名称并返回存在的第一个模板。

如果加载模板失败,则可能会引发 django.template 中定义的以下两个异常:

exception TemplateDoesNotExist(msg, tried=None, backend=None, chain=None)
当找不到模板时会引发此异常。 它接受以下可选参数,用于在调试页面上填充 模板 postmortem:
backend
产生异常的模板后端实例。
tried
查找模板时尝试的源列表。 这被格式化为包含 (origin, status) 的元组列表,其中 origin 是一个 origin-like 对象,而 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 令牌在模板中可用。 如何实现取决于每个后端。

这是搜索算法的示例。 对于这个例子,:setting:`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')

使用与上面相同的 :setting:`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

A dict 用作模板的渲染上下文。

request

一个可选的 HttpRequest,它将在模板的渲染过程中可用。

using

一个可选的模板引擎 :setting:`名称 ` . 模板搜索将仅限于该引擎。

用法示例:

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' 在这个例子中 - 是引擎的 :setting:`名称 ` .

内置后端

class DjangoTemplates

放 :设置:`后端 ` 到'django.template.backends.django.DjangoTemplates'配置 Django 模板引擎。

什么时候 :设置:`APP_DIRS ` 是True ,DjangoTemplates 引擎在templates已安装应用程序的子目录。 保留这个通用名称是为了向后兼容。

DjangoTemplates引擎接受以下内容 :设置:`选项 ` :

  • 'autoescape':控制是否启用 HTML 自动转义的布尔值。

    默认为 True。

    警告

    如果您要呈现非 HTML 模板,则仅将其设置为 False!

  • 'context_processors':可调用对象的虚线 Python 路径列表,用于在使用请求呈现模板时填充上下文。 这些可调用对象将请求对象作为其参数,并返回要合并到上下文中的项目的 dict。

    它默认为空列表。

    有关更多信息,请参阅 RequestContext。

  • 'debug':打开/关闭模板调试模式的布尔值。 如果是True,花哨的错误页面会显示模板渲染过程中出现的任何异常的详细报告。 此报告包含模板的相关片段,并突出显示了相应的行。

    它默认为 :setting:`DEBUG` 设置的值。

  • 'loaders':模板加载器类的虚线 Python 路径列表。 每个 Loader 类都知道如何从特定来源导入模板。 或者,可以使用元组代替字符串。 元组中的第一项应该是 Loader 类名,后续项在初始化时传递给 Loader。

    默认值取决于 :setting:`目录 ` 和 :设置:`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',
        },
    }

    可以通过将相应的字典键传递给库来加载库 :ttag:`{% 加载 %} ` 标签。

  • 'builtins':要添加到 built-ins 的模板标记模块的虚线 Python 路径列表。 例如:

    OPTIONS={
        'builtins': ['myapp.builtins'],
    }

    无需首先调用内置库中的标签和过滤器即可使用 :ttag:`{% 加载 %} ` 标签。

class Jinja2

需要安装 Jinja2:

放 :设置:`后端 ` 到'django.template.backends.jinja2.Jinja2'配置一个 Jinja2 引擎。

什么时候 :设置:`APP_DIRS ` 是True ,Jinja2 引擎在jinja2已安装应用程序的子目录。

最重要的入口 :设置:`选项 ` 是'environment' . 它是返回 Jinja2 环境的可调用对象的虚线 Python 路径。 默认为 'jinja2.Environment'。 Django 调用该 callable 并将其他选项作为关键字参数传递。 此外,Django 为一些选项添加了与 Jinja2 不同的默认值:

Jinja2引擎也接受以下内容 :设置:`选项 ` :

  • '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 测试等效的语言。


自定义后端

以下是如何实现自定义模板后端以使用另一个模板系统。 模板后端是一个继承 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。


自定义引擎的调试集成

Django 调试页面具有钩子,可在出现模板错误时提供详细信息。 自定义模板引擎可以使用这些钩子来增强向用户显示的回溯信息。 以下挂钩可用:

模板事后分析

当提出 TemplateDoesNotExist 时,会出现事后分析。 它列出了在尝试查找给定模板时使用的模板引擎和加载器。 例如,如果配置了两个 Django 引擎,事后分析将显示为:

../_images/postmortem.png 自定义引擎可以通过在引发 TemplateDoesNotExist 时传递 backend 和 tried 参数来填充事后分析。 使用事后分析 的后端应在模板对象上指定原点 。


上下文线路信息

如果在模板解析或渲染过程中发生错误,Django 可以显示发生错误的行。 例如:

../_images/template-lines.png 自定义引擎可以通过在解析和呈现期间引发的异常设置 template_debug 属性来填充此信息。 该属性是一个 dict,具有以下值:

  • 'name':发生异常的模板名称。
  • 'message':异常信息。
  • 'source_lines':发生异常的行之前、之后和包括行。 这是用于上下文的,因此它不应包含超过 20 行左右。
  • 'line':发生异常的行号。
  • 'before':在引发错误的标记之前的错误行上的内容。
  • 'during':引发错误的令牌。
  • 'after':在引发错误的标记之后的错误行上的内容。
  • '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,
}

Origin API 和第三方集成

Django 模板有一个 Origin 对象,可通过 template.origin 属性使用。 这使调试信息能够显示在 模板 postmortem 中,以及在 3rd-party 库中,如 Django 调试工具栏 。

自定义引擎可以通过创建指定以下属性的对象来提供自己的 template.origin 信息:

  • 'name':模板的完整路径。
  • 'template_name':传递给模板加载方法的模板的相对路径。
  • 'loader_name':标识用于加载模板的函数或类的可选字符串,例如 django.template.loaders.filesystem.Loader。


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" }}

提供了内置过滤器 的 参考以及编写自定义过滤器 的 说明。


评论

评论看起来像这样:

{# this won't be rendered #}

一种 :ttag:`{% 评论 %} ` 标签提供多行注释。


组件

关于本节

这是 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 对象。

Django 提供了多个 内置模板加载器 并支持 自定义模板加载器 。


上下文处理器

上下文处理器是接收当前 HttpRequest 作为参数并返回要添加到渲染上下文的数据 dict 的函数。

它们的主要用途是将所有模板共享的公共数据添加到上下文中,而无需在每个视图中重复代码。

Django 提供了许多 内置上下文处理器 。 实现自定义上下文处理器就像定义一个函数一样简单。