Django模板语言

本文档解释了Django模板系统的语言语法。 如果您正在寻找关于如何工作以及如何扩展它的技术更深入的观点,请参阅The Django template language: for Python programmers

Django的模板语言旨在在权力和易用性之间取得平衡。 它的设计让那些习惯使用HTML的人感觉舒适。 If you have any exposure to other text-based template languages, such as Smarty or Jinja2, you should feel right at home with Django’s templates.

哲学

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

The Django template system provides tags which function similarly to some programming constructs – an if tag for boolean tests, a for tag for looping, etc. – but these are not simply executed as the corresponding Python code, and the template system will not execute arbitrary Python expressions. 只有下面列出的标签,过滤器和语法是默认支持的(尽管你可以根据需要在模板语言中添加your own extensions)。

模板¶ T0>

模板只是一个文本文件。 它可以生成任何基于文本的格式(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是虐待行为!

变量¶ T0>

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

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

在幕后

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

  • 字典查找
  • 属性或方法查找
  • 数字索引查找

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

此查找顺序可能会导致一些意外行为,其中的对象会覆盖字典查找。 例如,考虑下面的代码片段,它试图循环collections.defaultdict

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

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

In the above example, {{ section.title }} will be replaced with the title attribute of the section object.

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

请注意,像{{ foo.bar }}这样的模板表达式中的“bar”将被解释为如果在模板上下文中存在,则不使用变量“bar”的值。

过滤器¶ T0>

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

过滤器如下所示:{{ name | lower }} This displays the value of the {{ name }} variable after being filtered through the lower filter, which converts text to lowercase. 使用管道(|)应用过滤器。

过滤器可以“链接”。一个过滤器的输出应用于下一个过滤器。 {{ text|escape|linebreaks }} is a common idiom for escaping text contents, then converting line breaks to <p> tags.

一些过滤器需要参数。 过滤器参数如下所示:{{ bio | truncatewords:30 }} 这将显示bio变量​​的前30个单词。

必须引用包含空格的过滤器参数;例如,加入一个逗号和空格的列表,您可以使用{{ list | join:“, }} T4> T0>。

Django提供了大约60个内置的模板过滤器。 您可以在built-in filter reference中阅读所有关于它们的内容。 为了让您了解可用的功能,以下是一些更常用的模板过滤器:

默认

如果一个变量是错误或空的,使用给定的默认值。 否则,使用变量的值。 例如:

{{ value|default:"nothing" }}

如果value未提供或为空,则上面将显示“nothing”。

长度

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

{{ value|length }}

If value is ['a', 'b', 'c', 'd'], the output will be 4.

filesizeformat

Formats the value like a “human-readable” file size (i.e. '13 KB', '4.1 MB', '102 bytes', etc.). 例如:

{{ value|filesizeformat }}

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

再次,这些只是一些例子。请参阅built-in filter reference了解完整列表。

您也可以创建自己的自定义模板过滤器;请参阅Custom template tags and filters

也可以看看

Django的管理界面可以包含一个给定站点可用的所有模板标签和过滤器的完整参考。 请参阅The Django admin documentation generator

标记¶ T0>

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

一些标签需要开始和结束标签(即 {% 标签 %} ... 标签 内容 ... {% ENDTAG %})。

Django附带大约二十个内置模板标签。 您可以在built-in tag reference中阅读所有关于它们的内容。 为了让您了解可用的功能,以下是一些比较常用的标签:

对于

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

<ul>
{% for athlete in athlete_list %}
    <li>{{ athlete.name }}</li>
{% endfor %}
</ul>
if, elif, and else

评估一个变量,如果该变量为“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 %}

In the above, if athlete_list is not empty, the number of athletes will be displayed by the {{ athlete_list|length }} variable. 否则,如果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
设置模板继承(见下文),这是减少模板中“样板”的强大方法。

再次,以上只是整个列表的一个选择;查看完整列表的built-in tag reference

您也可以创建自己的自定义模板标签;请参阅Custom template tags and filters

也可以看看

Django的管理界面可以包含一个给定站点可用的所有模板标签和过滤器的完整参考。 请参阅The Django admin documentation generator

注释¶ T0>

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

例如,这个模板会呈现为'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块,因此将使用父模板的值。 始终使用父模板中{% %}标记中的内容作为后备。

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

  • 创建一个包含网站主外观的base.html模板。
  • 为您的网站的每个“部分”创建一个base_SECTIONNAME.html模板。 例如,base_news.htmlbase_sports.html 这些模板都可以扩展base.html,并包含特定于部分的样式/设计。
  • 为每种类型的页面创建单独的模板,例如新闻文章或博客条目。 这些模板扩展了适当的部分模板。

这种方法最大限度地实现了代码的重用,并且可以轻松地将项目添加到共享内容区域,例如部分范围的导航。

这里有一些使用继承的技巧:

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

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

  • 如果您发现自己在多个模板中复制了内容,则可能意味着您应该将该内容移动到{% %} t3 >在父模板中。

  • 如果您需要从父模板中获取块的内容,那么{{ block.super }} t0 >变量会做的伎俩。 如果你想添加一个父块的内容,而不是完全覆盖它,这是很有用的。 使用{{ block.super }}插入的数据不会自动转义(参见next部分),因为它已经被转义了,如果有必要,在父模板中。

  • {% block %}之外使用模板标记as创建的变量语法不能在块内部使用。 例如,这个模板不会渲染任何东西:

    {% trans "Title" as title %}
    {% block content %}{{ title }}{% endblock %}
    
  • For extra readability, you can optionally give a name to your {% endblock %} tag. 例如:

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

    在较大的模板中,这种方法可以帮助您查看哪些{% %}标签正在关闭。

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

自动HTML转义

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

Hello, {{ name }}

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

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

有了这个名字值,这个模板会被渲染为:

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

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

同样的,如果这个名字包含一个'<'符号,像这样?

<b>username

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

Hello, <b>username

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

显然,用户提交的数据不应该盲目地被信任,并直接插入到您的网页中,因为恶意用户可能会使用这种漏洞做可能的坏事。 This type of security exploit is called a Cross Site Scripting (XSS) attack.

为了避免这个问题,你有两个选择:

  • 其一,你可以确保通过escape过滤器(下面介绍)运行每个不受信任的变量,这个过滤器可能将有害的HTML字符转换为无害的字符。 This was the default solution in Django for its first few years, but the problem is that it puts the onus on you, the developer / template author, to ensure you’re escaping everything. 忘记逃避数据很容易。
  • 二,你可以利用Django的自动HTML转义。 本节的其余部分将介绍自动转义的工作原理。

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

  • <转换为&lt;
  • >转换为&gt;
  • ' (single quote) is converted to &#39;
  • " (double quote) is converted to &quot;
  • &转换为&amp;

我们再一次强调,这种行为默认是开启的。 如果您使用的是Django的模板系统,那么您将受到保护。

如何关闭

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

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

对于个别变量

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

This will be escaped: {{ data }}
This will not be escaped: {{ data|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>

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

备注¶ T0>

一般来说,模板作者不需要担心很多自动转义。 Python方面的开发人员(编写视图和自定义过滤器的人)需要考虑数据不应该被转义的情况,并适当地标记数据,以便在模板中使用Just Work。

如果您创建的模板可能在不确定是否启用了自动转义的情况下使用,则可以将escape过滤器添加到需要转义的任何变量中。 当自动转义打开时,没有escape过滤器双重转义数据的危险 - 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”语法。 因此,给定一个名为“comment”的模型,并将其与称为“task”的模型建立外键关系,您可以循环执行给定任务的所有注释,如下所示:

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

Similarly, QuerySets provide a count() method to count the number of objects they contain. 因此,您可以通过以下方式获得与当前任务相关的所有评论的计数:

{{ 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 %}

有关编写自定义模板库的信息,请参阅Custom template tags and filters

自定义库和模板继承

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

For example, if a template foo.html has {% load humanize %}, a child template (e.g., one that has {% extends "foo.html" %}) will not have access to the humanize template tags and filters. 子模板负责自己的{% 加载 人性化 %}

这是为了可维护性和完整性的一个特征。

也可以看看

模板参考
内置标签,内置过滤器,使用替代模板,语言等等。