模板

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

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

{% comment %} 标签提供多行注释。

组件

关于本节

这是 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 提供了许多 内置上下文处理器,你也可以实现自己的附加上下文处理器。

对模板引擎的支持

配置

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

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

BACKEND 是一个点分 Python 路径,指向实现 Django 模板后端 API 的模板引擎类。内置后端是 django.template.backends.django.DjangoTemplatesdjango.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)

当找到模板但其中包含错误时,会引发此异常。

Template 对象由 get_template()select_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 中。

最后,你可以直接使用已配置的引擎

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_DIRSTrue 时,DjangoTemplates 引擎在已安装应用程序的 templates 子目录中查找模板。保留此通用名称是为了向后兼容。

DjangoTemplates 引擎接受以下 OPTIONS

  • 'autoescape':一个布尔值,用于控制是否启用 HTML 自动转义。

    其默认值为 True

    警告

    仅当您要呈现非 HTML 模板时才将其设置为 False

  • 'context_processors':一个圆点 Python 路径列表,指向在使用请求呈现模板时用于填充上下文的可调用对象。这些可调用对象以请求对象作为其参数,并返回一个 dict,其中包含要合并到上下文中项。

    默认为空列表。

    有关详细信息,请参阅 RequestContext

  • 'debug':一个布尔值,用于打开/关闭模板调试模式。如果为 True,则精美的错误页面将显示模板呈现期间引发的任何异常的详细报告。此报告包含模板的相关片段,并突出显示了相应的行。

    它默认为 DEBUG 设置的值。

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

    默认值取决于 DIRSAPP_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

$ python -m pip install Jinja2
...\> py -m pip install Jinja2

BACKEND 设置为 'django.template.backends.jinja2.Jinja2' 以配置 Jinja2 引擎。

APP_DIRSTrue 时,Jinja2 引擎在已安装应用程序的 jinja2 子目录中查找模板。

OPTIONS 中最重要的条目是 'environment'。它是返回 Jinja2 环境的可调用对象的 Python 点状路径。它默认为 'jinja2.Environment'。Django 调用该可调用对象并将其他选项作为关键字参数传递。此外,Django 为少数选项添加了与 Jinja2 不同的默认值

  • 'autoescape'True
  • 'loader': 为 DIRSAPP_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 后端会将全局变量 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 支持在模板中将参数传递给可调用对象,因此可以通过在 Jinja2 模板中调用函数来实现许多在 Django 模板中需要模板标签或过滤器的功能,如上例所示。Jinja2 的全局命名空间消除了对模板上下文处理器的需求。Django 模板语言没有与 Jinja2 测试等效的内容。

返回顶部