模板 — 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.DjangoTemplatesdjango.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 后端将全局变量 requestcsrf_inputcsrf_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 时传递 backendtried 参数来填充事后分析。 使用事后分析 的后端应在模板对象上指定原点


上下文线路信息

如果在模板解析或渲染过程中发生错误,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.RequestContextContext 的子类,它存储当前的 HttpRequest 并运行模板上下文处理器。

通用 API 没有等效的概念。 上下文数据在一个普通的 dict 中传递,如果需要,当前的 HttpRequest 将单独传递。


装载机

模板加载器负责定位模板,加载它们,并返回 Template 对象。

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


上下文处理器

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

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

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