Skip to main content

Django模板语言

本文档介绍了Django模板系统的语言语法。如果你正在寻找一个更技术的角度来看它是如何工作和如何扩展它,请参阅 Django模板语言:用于Python程序员

Django的模板语言旨在平衡权力和轻松。它的设计使那些习惯于使用HTML的人感到舒适。如果你有任何暴露其他基于文本的模板语言,如 SmartyJinja2,你应该觉得在家里与Django的模板。

哲学

如果你有编程的背景,或者如果你习惯于将编程代码直接混合到HTML中的语言,你应该记住,Django模板系统不是简单的Python嵌入到HTML。这是设计:模板系统是表示表示,而不是程序逻辑。

Django模板系统提供的标签的功能类似于一些编程结构 - 一个用于布尔测试的 if 标签,一个用于循环的 for 标签等等 - 但是这些标签不是作为相应的Python代码简单执行的,模板系统不会执行任意Python表达式。默认情况下只支持下面列出的标记,过滤器和语法(虽然您可以根据需要向模板语言添加 您自己的扩展)。

模板

模板只是一个文本文件。它可以生成任何基于文本的格式(HTML,XML,CSV等)。

模板包含 变量,它在模板被求值时被替换为值,标签 控制模板的逻辑。

下面是一个最基本的模板,说明了一些基础知识。每个元素将在本文档的后面解释。

{% extends "base_generic.html" %}

{% block title %}{{ section.title }}{% endblock %}

{% block content %}
<h1>{{ section.title }}</h1>

{% for story in story_list %}
<h2>
  <a href="{{ story.get_absolute_url }}">
    {{ story.headline|upper }}
  </a>
</h2>
<p>{{ story.tease|truncatewords:"100" }}</p>
{% endfor %}
{% endblock %}

哲学

为什么使用基于文本的模板而不是基于XML的模板(如Zope的TAL)?我们希望Django的模板语言不仅仅适用于XML/HTML模板。在世界在线,我们使用它的电子邮件,JavaScript和CSV。您可以对任何基于文本的格式使用模板语言。

哦,还有一件事:让人类编辑XML是虐待狂!

变量

变量如下所示:{{ variable }}。当模板引擎遇到变量时,它将评估该变量并将其替换为结果。变量名称由字母数字字符和下划线("_")的任意组合组成。点(".")也出现在变量部分,尽管它有特殊的意义,如下所示。重要的是,您不能在变量名称中有空格或标点符号。

使用点(.)访问变量的属性。

幕后

从技术上讲,当模板系统遇到点时,它会按照以下顺序尝试以下查找:

  • 字典查找

  • 属性或方法查找

  • 数字索引查找

如果结果值是可调用的,则调用它时不带参数。调用的结果变为模板值。

此查找顺序可能会对覆盖字典查找的对象造成某些意外行为。例如,考虑以下代码片段尝试循环 collections.defaultdict:

{% for k, v in defaultdict.iteritems %}
    Do something with k and v here...
{% endfor %}

因为字典查找首先发生,该行为启动并提供默认值,而不是使用预期的 .iteritems() 方法。在这种情况下,请考虑先转换为字典。

在上面的例子中,{{ section.title }} 将被 section 对象的 title 属性替换。

如果使用不存在的变量,模板系统将插入 string_if_invalid 选项的值,默认情况下,该选项设置为 '' (空字符串)。

请注意,模板表达式(如 {{ foo.bar }})中的“bar”将被解释为文字字符串,而不使用变量“bar”的值(如果模板上下文中存在)。

过滤器

您可以使用 过滤器 修改要显示的变量。

过滤器如下所示:{{ name|lower }}。这将显示通过 lower 过滤器过滤后的 {{ name }} 变量的值,该过滤器将文本转换为小写。使用管道(|)应用过滤器。

过滤器可以“链接”。一个滤波器的输出应用于下一个滤波器。 {{ text|escape|linebreaks }} 是转义文本内容,然后将换行符转换为 <p> 标签的常见习语。

一些过滤器接受参数。过滤器参数看起来像这样:{{ bio|truncatewords:30 }}。这将显示 bio 变量的前30个字。

包含空格的过滤器参数必须加引号;例如,要使用逗号和空格连接列表,您将使用 {{ list|join:", " }}

Django提供了大约60个内置的模板过滤器。你可以在 内置滤波器参考 阅读所有关于他们。为了让您了解可用的功能,以下是一些更常用的模板过滤器:

default

如果变量为false或为空,请使用给定的默认值。否则,使用变量的值。例如:

{{ value|default:"nothing" }}

如果没有提供 value 或空白,上面将显示“ nothing ”。

length

返回值的长度。这适用于字符串和列表。例如:

{{ value|length }}

如果 value['a', 'b', 'c', 'd'],输出将是 4

filesizeformat

将该值格式化为“人类可读”文件大小(即 '13 KB''4.1 MB''102 bytes' 等)。例如:

{{ value|filesizeformat }}

如果 value 是123456789,则输出将是 117.7 MB

同样,这些只是几个例子;请参阅 内置滤波器参考 了解完整列表。

您还可以创建自己的自定义模板过滤器;见 自定义模板代码和过滤器

参见

Django管理界面可以包含对给定网站可用的所有模板标记和过滤器的完整引用。见 Django管理文档生成器

标签

标签看起来像这样:{% tag %}。标签比变量更复杂:一些在输出中创建文本,一些控制流通过执行循环或逻辑,一些将外部信息加载到模板以供稍后的变量使用。

一些标签需要开始和结束标签(即 {% tag %} ... tag contents ... {% endtag %})。

Django附带大约二十个内置模板标签。你可以在 内置标记引用 阅读所有关于他们。为了给你一个可用的味道,这里是一些更常用的标签:

for

循环数组中的每个项目。例如,显示 athlete_list 中提供的运动员的列表:

<ul>
{% for athlete in athlete_list %}
    <li>{{ athlete.name }}</li>
{% endfor %}
</ul>
ifelifelse

评估变量,如果该变量为“true”,则显示块的内容:

{% if athlete_list %}
    Number of athletes: {{ athlete_list|length }}
{% elif athlete_in_locker_room_list %}
    Athletes should be out of the locker room soon!
{% else %}
    No athletes.
{% endif %}

在上述中,如果 athlete_list 不为空,则运动员的数量将由 {{ athlete_list|length }} 变量显示。否则,如果 athlete_in_locker_room_list 不为空,将显示消息“运动员应该出...”。如果两个列表都为空,“没有运动员”。将显示。

您还可以在 if 标记中使用过滤器和各种运算符:

{% if athlete_list|length > 1 %}
   Team: {% for athlete in athlete_list %} ... {% endfor %}
{% else %}
   Athlete: {{ athlete_list.0.name }}
{% endif %}

虽然上面的示例工作,请注意,大多数模板过滤器返回字符串,因此使用过滤器的数学比较一般不会按预期工作。 length 是一个例外。

blockextends

设置 template inheritance (见下文),一种强大的方法,减少模板中的“样板”。

同样,上面只是对整个列表的选择;请参阅 内置标记引用 了解完整列表。

您还可以创建自己的自定义模板标记;见 自定义模板代码和过滤器

参见

Django管理界面可以包含对给定网站可用的所有模板标记和过滤器的完整引用。见 Django管理文档生成器

注释

要注释掉模板中某一行的一部分,请使用注释语法:{# #}

例如,此模板将呈现为 'hello':

{# greeting #}hello

注释可以包含任何模板代码,无效或不。例如:

{# {% if foo %}bar{% else %} #}

此语法只能用于单行注释(在 {##} 分隔符之间不允许有换行符)。如果您需要注释掉模板的多行部分,请参阅 comment 标记。

模板继承

Django模板引擎中最强大的,最复杂的部分是模板继承。模板继承允许您构建一个基本的“框架”模板,其中包含站点的所有常见元素,并定义子模板可以覆盖的

从一个例子开始,最容易理解模板继承:

<!DOCTYPE html>
<html lang="en">
<head>
    <link rel="stylesheet" href="style.css" />
    <title>{% block title %}My amazing site{% endblock %}</title>
</head>

<body>
    <div id="sidebar">
        {% block sidebar %}
        <ul>
            <li><a href="/">Home</a></li>
            <li><a href="/blog/">Blog</a></li>
        </ul>
        {% endblock %}
    </div>

    <div id="content">
        {% block content %}{% endblock %}
    </div>
</body>
</html>

这个模板(我们称之为 base.html)定义了一个简单的HTML骨架文档,您可以将其用于简单的两列页面。它是“孩子”模板的工作,用内容填充空块。

在该示例中,block 标签定义了子模板可以填充的三个块。所有 block 标签都要告诉模板引擎子模板可以覆盖模板的那些部分。

子模板可能如下所示:

{% extends "base.html" %}

{% block title %}My amazing blog{% endblock %}

{% block content %}
{% for entry in blog_entries %}
    <h2>{{ entry.title }}</h2>
    <p>{{ entry.body }}</p>
{% endfor %}
{% endblock %}

extends 标签是这里的关键。它告诉模板引擎这个模板“扩展”另一个模板。当模板系统评估此模板时,首先它定位父 - 在这种情况下,“base.html”。

此时,模板引擎将注意到 base.html 中的三个 block 标签,并用子模板的内容替换这些块。根据 blog_entries 的值,输出可能类似:

<!DOCTYPE html>
<html lang="en">
<head>
    <link rel="stylesheet" href="style.css" />
    <title>My amazing blog</title>
</head>

<body>
    <div id="sidebar">
        <ul>
            <li><a href="/">Home</a></li>
            <li><a href="/blog/">Blog</a></li>
        </ul>
    </div>

    <div id="content">
        <h2>Entry one</h2>
        <p>This is my first entry.</p>

        <h2>Entry two</h2>
        <p>This is my second entry.</p>
    </div>
</body>
</html>

请注意,由于子模板未定义 sidebar 块,因此将使用父模板中的值。父模板中 {% block %} 标记内的内容始终用作后备。

您可以根据需要使用尽可能多的继承级别。使用继承的一种常见方法是以下三级方法:

  • 创建一个包含您网站主要外观的 base.html 模板。

  • 为您网站的每个“部分”创建一个 base_SECTIONNAME.html 模板。例如,base_news.htmlbase_sports.html。这些模板都扩展了 base.html 并包括节特定的样式/设计。

  • 为每种类型的网页创建单独的模板,例如新闻文章或博客条目。这些模板扩展适当的部分模板。

这种方法使代码重用最大化,并且可以很容易地将项目添加到共享内容区域,例如部分范围的导航。

这里有一些使用继承的提示:

  • 如果在模板中使用 {% extends %},它必须是该模板中的第一个模板标记。模板继承将不工作,否则。

  • 在您的基本模板中更多的 {% block %} 标签更好。请记住,子模板不必定义所有父块,因此您可以在多个块中填写合理的默认值,然后只定义稍后需要的默认值。最好有更多的钩子比更少的钩子。

  • 如果你发现自己在许多模板中复制内容,这可能意味着你应该将该内容移动到父模板中的 {% block %}

  • 如果你需要从父模板获取块的内容,{{ block.super }} 变量将会执行。如果要添加到父块的内容而不是完全覆盖它,这是非常有用的。使用 {{ block.super }} 插入的数据不会自动转义(参见 next section),因为在父模板中已经转义了(如果必要)。

  • 为了提高可读性,您可以选择为您的 {% endblock %} 标签提供 name。例如:

    {% block content %}
    ...
    {% endblock content %}
    

    在较大的模板中,此技术可帮助您查看正在关闭哪些 {% block %} 标记。

最后,请注意,您不能在同一模板中定义具有相同名称的多个 block 标记。存在此限制,因为块标记在“两个”方向工作。也就是说,块标签不仅仅提供要填充的洞 - 它还定义填充 parent 中的洞的内容。如果在模板中有两个类似命名的 block 标签,那么该模板的父级不会知道要使用哪个块的内容。

自动HTML转义

从模板生成HTML时,始终存在一个风险,即变量将包含影响生成的HTML的字符。例如,考虑这个模板片段:

Hello, {{ name }}

首先,这似乎是一个无害的方式来显示用户的名字,但考虑如果用户输入他们的名字,如果这样做会发生什么:

<script>alert('hello')</script>

使用此名称值,模板将呈现为:

Hello, <script>alert('hello')</script>

...这意味着浏览器会弹出一个JavaScript警告框!

同样,如果名称包含 '<' 符号,如果这样?

<b>username

这将导致像这样的渲染模板:

Hello, <b>username

...这反过来,将导致网页的其余部分被加粗!

显然,用户提交的数据不应盲目信任并直接插入您的网页,因为恶意用户可能使用这种洞做潜在的坏事。这种类型的安全漏洞被称为 Cross Site Scripting (XSS)攻击。

为了避免此问题,您有两个选项:

  • 一个,你可以确保通过 escape 过滤器(如下文所述)运行每个不受信任的变量,它将潜在的有害HTML字符转换为无害的字符。这是Django最初几年的默认解决方案,但问题是它将 you,开发人员/模板作者,以确保你逃避一切。很容易忘记逃离数据。

  • 二,你可以利用Django的自动HTML转义。本节的其余部分将介绍自动转义的工作原理。

默认情况下,在Django中,每个模板自动转义每个变量标签的输出。具体来说,这五个字符被转义:

  • < 转换为 &lt;

  • > 转换为 &gt;

  • ' (单引号)转换为 &#39;

  • " (双引号)转换为 &quot;

  • & 转换为 &amp;

同样,我们强调这种行为默认情况下是开启的。如果你使用Django模板系统,你受到保护。

如何关闭它

如果不希望自动转义数据,在每个站点,每个模板级别或每个变量级别,您可以通过多种方式关闭它。

为什么要关闭它?因为有时,模板变量包含的数据是您要将 intend 呈现为原始HTML,在这种情况下,您不希望对其内容进行转义。例如,您可以在数据库中存储一个HTML块,并希望将其直接嵌入到模板中。或者,您可能使用Django的模板系统来生成 not HTML的文本 - 例如电子邮件。

对于单个变量

要禁用单个变量的自动转义,请使用 safe 过滤器:

This will be escaped: {{ data }}
This will not be escaped: {{ data|safe }}

safe 视为 安全从进一步逃脱可以安全地解释为HTML 的缩写。在这个例子中,如果 data 包含 '<b>',输出将是:

This will be escaped: &lt;b&gt;
This will not be escaped: <b>

对于模板块

要控制模板的自动转义,请在 autoescape 标记中包装模板(或仅包含模板的特定部分),例如:

{% autoescape off %}
    Hello {{ name }}
{% endautoescape %}

autoescape 标记采用 onoff 作为其参数。有时,您可能想强制自动转义,否则将被禁用。这里是一个示例模板:

Auto-escaping is on by default. Hello {{ name }}

{% autoescape off %}
    This will not be auto-escaped: {{ data }}.

    Nor this: {{ other_data }}
    {% autoescape on %}
        Auto-escaping applies again: {{ name }}
    {% endautoescape %}
{% endautoescape %}

自动转义标记将其效果传递到扩展当前模板的模板以及通过 include 标签包含的模板,就像所有块标签一样。例如:

base.html
{% autoescape off %}
<h1>{% block title %}{% endblock %}</h1>
{% block content %}
{% endblock %}
{% endautoescape %}
child.html
{% extends "base.html" %}
{% block title %}This &amp; that{% endblock %}
{% block content %}{{ greeting }}{% endblock %}

由于自动转义在基本模板中已关闭,因此在子模板中也将关闭自动转义,从而在 greeting 变量包含字符串 <b>Hello!</b> 时生成以下呈现的HTML:

<h1>This &amp; that</h1>
<b>Hello!</b>

笔记

通常,模板作者不需要担心自动转义非常。 Python侧面的开发人员(编写视图和自定义过滤器的人)需要考虑不应该转义数据的情况,并且适当地标记数据,所以只是在模板中工作。

如果您创建的模板可能用于不确定是否启用自动转义的情况,请将 escape 过滤器添加到需要转义的任何变量。当自动转义打开时,没有 escape 过滤器 double-escaping 数据的危险–escape 过滤器不会影响自动转义的变量。

字符串文字和自动转义

正如我们前面提到的,过滤器参数可以是字符串:

{{ data|default:"This is a string literal." }}

所有字符串文字都插入 没有 任何自动转义到模板中 - 它们就好像它们都通过 safe 过滤器。这背后的原因是,模板作者控制着什么进入字符串文字,所以他们可以确保文本在模板写入时正确转义。

这意味着你会写

{{ data|default:"3 &lt; 2" }}

...而不是:

{{ data|default:"3 < 2" }}  {# Bad! Don't do this. #}

这不会影响来自变量本身的数据发生什么。如果需要,变量的内容仍然会自动转义,因为它们超出了模板作者的控制。

访问方法调用

大多数附加到对象的方法调用也可从模板中获取。这意味着模板可以访问的不仅仅是类属性(如字段名)和从视图传入的变量。例如,Django ORM提供了用于查找与外键相关的对象集合的 “entry_set” 语法。因此,给定一个称为“注释”的模型与一个称为“任务”的模型具有外键关系,你可以循环遍历给定任务的所有注释,像这样:

{% for comment in task.comment_set.all %}
    {{ comment }}
{% endfor %}

类似地,QuerySets 提供 count() 方法来计算它们包含的对象的数量。因此,您可以获取与当前任务相关的所有注释的计数:

{{ task.comment_set.all.count }}

当然,您可以轻松地访问您在自己的模型上明确定义的方法:

models.py
class Task(models.Model):
    def foo(self):
        return "bar"
template.html
{{ task.foo }}

因为Django有意地限制了模板语言中可用的逻辑处理量,所以不可能将参数传递给从模板中访问的方法调用。数据应在视图中计算,然后传递到模板以显示。

自定义标记和过滤器库

某些应用程序提供自定义标记和过滤器库。要在模板中访问它们,请确保应用程序在 INSTALLED_APPS 中(我们将为此示例添加 'django.contrib.humanize'),然后在模板中使用 load 标记:

{% load humanize %}

{{ 45000|intcomma }}

在上面,load 标签加载 humanize 标签库,然后使 intcomma 过滤器可供使用。如果已启用 django.contrib.admindocs,可以查看管理员中的文档区域,以查找安装中的自定义库列表。

load 标记可以使用多个库名称,用空格分隔。例:

{% load humanize i18n %}

有关编写自己的自定义模板库的信息,请参阅 自定义模板代码和过滤器

自定义库和模板继承

加载自定义标记或过滤器库时,标记/过滤器只能用于当前模板,而不能用于模板继承路径中的任何父模板或子模板。

例如,如果模板 foo.html 具有 {% load humanize %},则子模板(例如,具有 {% extends "foo.html" %} 的子模板)将 not 访问人源化模板标签和过滤器。子模板负责自己的 {% load humanize %}

这是一个功能,为了可维护性和健全。

参见

模板参考

涵盖内置标记,内置过滤器,使用替代模板,语言等。