The Django template languageP

本文档介绍了Django模板系统语言的语法. 如果你希望从一个更技术的角度来看它是如何工作的和如何扩展它, 请参阅 The Django template language: for Python programmers.

Django's template language is designed to strike a balance between power and ease. It's designed to feel comfortable to those used to working with 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.

设计哲学

如果你有编程背景,或者你使用过PHP等嵌入HTML的程序语言,你要牢记, Django的模板系统是不是简单的把Python嵌入到HTML中. 它的设计是: 模板系统旨在展示内容, 而不是程序逻辑.

jango的模板系统提供的标签, 其功能类似于一些编程结构 -- if 标签用于布尔值测试, for 标签用来做for循环, 等等.但这些都不是简单的执行相应的Python代码,模板系统不会执行任意的Python表达式. 通常情况下, 模板系统仅支持接下来列出的这些标签和过滤 (仅当需要时你可以添加 你自己的扩展 到模板语法中).

模板P

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

模板中包含的 变量 被替换为变量的值, 标签 被替换为相应的模板控制逻辑.

Below is a minimal template that illustrates a few basics. Each element will be explained later in this document.

{% 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中. 您可以在任何文本格式中使用它.

Oh, and one more thing: making humans edit XML is sadistic!

变量P

Variables look like this: {{ variable }}. When the template engine encounters a variable, it evaluates that variable and replaces it with the result. Variable names consist of any combination of alphanumeric characters and the underscore ("_") but may not start with an underscore. The dot (".") also appears in variable sections, although that has a special meaning, as indicated below. Importantly, you cannot have spaces or punctuation characters in variable names.

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

幕后

从技术上讲,当模板系统遇到一个点,它会尝试下面的动作,顺序如下:

  • 字典查询
  • Attribute or method lookup
  • Numeric index lookup

If the resulting value is callable, it is called with no arguments. The result of the call becomes the template value.

This lookup order can cause some unexpected behavior with objects that override dictionary lookup. For example, consider the following code snippet that attempts to loop over a collections.defaultdict:

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

Because dictionary lookup happens first, that behavior kicks in and provides a default value instead of using the intended .items() method. In this case, consider converting to a dictionary first.

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

If you use a variable that doesn't exist, the template system will insert the value of the string_if_invalid option, which is set to '' (the empty string) by default.

Note that "bar" in a template expression like {{ foo.bar }} will be interpreted as a literal string and not using the value of the variable "bar", if one exists in the template context.

Variable attributes that begin with an underscore may not be accessed as they're generally considered private.

过滤器P

您可以通过使用 过滤器 修改变量的显示.

过滤器用起来像这样: {{ name|lower }} . 通过过滤器 lower 变量 {{ name }} 变为了小写字符. 通过管道符 (|) 间隔变量和过滤器来使用过滤器.

过滤器可以是 "链式的". 一个过滤器的输出作为下一个过滤器的输入. {{ text|escape|linebreaks }} 是一种常用的转换方式, 在这之后换行符被替换为了 <p> 标签.

有些过滤器是带有参数的. 一个带参数的过滤器看起来像这样: {{ bio|truncatewords:30 }}. 他会显示 bio 变量的前30个字符.

Filter arguments that contain spaces must be quoted; for example, to join a list with commas and spaces you'd use {{ list|join:", " }}.

Django provides about sixty built-in template filters. You can read all about them in the built-in filter reference. To give you a taste of what's available, here are some of the more commonly used template filters:

default

If a variable is false or empty, use given default. Otherwise, use the value of the variable. For example:

{{ value|default:"nothing" }}

如果 value 没有提供或者为空,那么他将显示为 "nothing" .

length

Returns the length of the value. This works for both strings and lists. For example:

{{ value|length }}

如果 value['a', 'b', 'c', 'd'], 那么他将被显示为 4.

filesizeformat

Formats the value like a "human-readable" file size (i.e. '13 KB', '4.1 MB', '102 bytes', etc.). For example:

{{ value|filesizeformat }}

If value is 123456789, the output would be 117.7 MB.

这些只是几个例子,请参阅 内置过滤器参考 的完整列表.

您还可以创建自己的自定义模板过滤器,请参见 自定义模板(template)的标签(tags)和过滤器(filters).

参见

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

标签(Tags)P

标签看起来像这样: {% tag %}. 标签要比变量更复杂: 有些标签会创建一段文本, 有些标签会控制输出流的方式比如循环或逻辑条件, 还有些标签会添加一些扩展内容到模板中, 比如另外的标签和过滤器.

有些标签在使用时需要有开始和结束标签 (像这样 {% tag %} ... 内容 ... {% endtag %}).

Django内置了大约二十几个标签. 你可以在 内置标签参考 阅读有关内置标签的内容. 为了让你尝试有什么可用的, 这里有一些常用标签的例子:

for

循环数组中的每个元素. 比如, 显示列表 athlete_list 中每个元素的 name 属性:

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

判断一个变量的布尔值, 如果它为 "true" 则显示 if 块儿内的内容, 否则显示 else 块儿内的文字:

{% 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. Otherwise, if athlete_in_locker_room_list is not empty, the message "Athletes should be out..." will be displayed. If both lists are empty, "No athletes." will be displayed.

也可以在 if 标签里使用过滤器和各种操作符:

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

While the above example works, be aware that most template filters return strings, so mathematical comparisons using filters will generally not work as you expect. length is an exception.

block and extends
请看 模板继承 (在下面), 一个强大的方法, 在模板上切割下来的"样板".

以上的例子只是整个标签列表的一小部分; 请参看 内置标签参考 完整阅览.

您还可以创建自己的自定义模板标签, 请参看 自定义模板(template)的标签(tags)和过滤器(filters).

参见

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

注释(Comments)P

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

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

{# greeting #}hello

注释可以包含任何模板代码, 使其生效或无效. 例如:

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

这个语法只能用于单行注释 (不允许在 {##} 之间换行). 如果您需要注释掉多行部分的模板, 参见 comment 标签.

模板继承P

Django模板中最强大和最复杂的部分就是模板继承. 模板继承允许你建立一个基本的"骨架"模板, 它包含了你网站所有常见的元素,并定义了可以被子模板覆盖的 块(blocks) .

这是最简单的理解模板继承的一个例子:

<!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 标签定义了三个可以被子模板填充的块. 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.html, base_sports.html. 这些模板都继承 base.html 模板. 这些模板中包含特定的设计/风格.
  • 为每一种类型的页面创建一个模板, 比如 news articleblog 内容. 这些模板扩展上一级模板的相应分类.

这种方法最大限度地重用了代码并能很容易将元素添加到公用的区域, 比如分类导航.

这里有一些关于模板继承的提示

  • 如果你在模板中使用 {% extends %}, 他必须位于模板的最开始. 如果在其他的部分声明, 则不生效.

  • 在你的基础模板中使用更多的 {% block %} 是一个好的方法. 请记住, 子模板不需要定义所有父模板中的块, 所以你可以在若干的块中填充默认值, 然后定义之后需要的块. 有更多的可用块总是更好的.

  • 如果你发现自己在许多模板中有重复内容的了, 这可能意味着你要移动这些内容到父模板的 {% block %} 中.

  • 如果你需要得到父模板块中的内容, 可以用 {{ block.super }} 变量. 这在你想使用父模板块中的内容又想在其中追加内容时是非常有用的. 使用 {{ block.super }} 插入的数据不会被自动转义 (参见 下一节), 因为它已经被转义了. 如果需要转义, 可以在父模板中转义.

  • Variables created outside of a {% block %} using the template tag as syntax can't be used inside the block. For example, this template doesn't render anything:

    {% trans "Title" as title %}
    {% block content %}{{ title }}{% endblock %}
    
  • 更好的可读性, 你可以给 {% endblock %} 标签定义一个 名字. 比如:

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

    在一个较长的模板中, 这个方法可以让你知道是哪一个 {% block %} 标签定义结束了.

最后请注意, 你不能在同一模板中定义多个具有相同名称的块标签. 存在这个限制是因为 block 标签是"双向"定义的. 也就是说, 它不仅指定了子模板要填充哪个"洞", 也说明了子模板要引用那些父模板的内容. 所以在有多个同名 block 标签时, 父模板就不知道到底要引用哪个块的内容了.

自动HTML转义P

从模板生成HTML时, 总是有一种风险, 即一个变量将会影响生成的HTML字符. 例如, 考虑这个模板片段:

Hello, {{ name }}

At first, this seems like a harmless way to display a user's name, but consider what would happen if the user entered their name as this:

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

当名字为这个值, 该模板将呈现为:

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

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

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

<b>username

这将导致呈现出这样的模板:

Hello, <b>username

...这意味着在此之后的文字将呈现为粗体!

显然, 用户提交的数据是不可靠的且不应被直接插入到您的网页, 因为恶意用户可以使用这种潜在的漏洞做不好的事情. 这种类型的安全漏洞被称为 Cross Site Scripting (跨站脚本) (XSS) 攻击.

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

  • 一, 确保让不受信任的变量经过了 escape 过滤器 (下文介绍), 将危险的HTML字符替换为无害的HTML转义字符. 这在Django最初时是默认的方案, 它使得 , 开发者, 有责任确保在必要时转义了变量. 但是人们很容易忽略它.
  • 二, 你可以用Django的自动HTML转义. 本节的其余部分介绍了自动转义如何工作.

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

  • < 被替换为 &lt;
  • > 被替换为 &gt;
  • ' (单引号) 被替换为 &#39;
  • " (双引号) 被替换为 &quot;
  • & 被替换为 &amp;

再次强调, 这种行为是默认的. 如果你使用的是Django的模板系统, 你是受保护的.

如何关闭它P

如果你不希望数据被自动转义, 你可以在站点, 模板, 变量这三个层面关闭它.

为什么你会想要把它关掉? 因为有时候, 你希望把模板变量中包含的数据作为原始的HTML渲染, 在这种情况下, 你不希望自己的内容被转义. 例如, 你可能在你的数据库的BLOB字段存储HTML, 并希望直接嵌入到您的模板. 或者你使用Django的模板系统生成不是HTML的文本 -- 比如电子邮件正文.

对于单个变量P

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

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

safe 可以被认为是 safe from further escapingcan be safely interpreted as HTML 的简写. 在这个例子中, 如果 data'<b>', 那么输出将会是:

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

对于模板文本块P

要在模板中控制自动转义, 可以在整个模板 (或者模板的特定区域) 使用 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 %}

The auto-escaping tag passes its effect onto templates that extend the current one as well as templates included via the include tag, just like all block tags. For example:

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

由于自动转义在base模板中被关闭了, 所以在子模板中他也是被关闭了的, 导致了在呈现HTML时, greeting 变量被输出为 <b>Hello!</b>:

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

注意P

一般情况下, 模板的开发者并不需要对自动转义太过关注. 这是编写Python的开发者 (写视图和写自定义模板标签的人) 需要考虑的事. 所以, 你只需要做和模板有关的活.

如果你不确定模板在何时会进行自动转义(或不进行), 那么就向所有需要转义的变量添加 escape 过滤器. 当自动转义被打开, 这么做不会有任何问题, escape 过滤器不会再次转义 -- escape 过滤器不会影响自动转义的变量.

字符串和自动转义P

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

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

这些作为过滤器参数的字符串 都不经过 自动转义, 他们是被认为经过了 safe 过滤器的. 这么做的原因是, 模板的开发者是在控制字符的输出, 所以他们能确保正确的使用转义后的HTML字面值.

这意味着你应当这么写

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

...rather than:

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

这不会影响来自变量本身的数据. 如果必要, 变量本身仍会被自动转义, 因为他们无法控制的模板作者.

访问方法调用P

大多附加到对象上的方法都可以在模板中被调用.这意味着模板可以从视图中传递过来变量中获得对象属性之外的值.例如, Django ORM 提供了 "entry_set" 语法寻找一个外键所关联的对象的集合. 因此, 架设一个叫 "comment" 的模型有外键指向了模型 "task",你可以通过给定一个实际的 task , 像这样循环输出它所有的 comment:

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

同样, QuerySets 提供了一个 count() 方法来统计它所包含的对象数目. 因此, 您可以得到当前 task 相关的所有 comment 的数目:

{{ task.comment_set.all.count }}

And of course you can easily access methods you've explicitly defined on your own models:

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

由于Django有意限制了模板中语言逻辑的处理, 所以不能在模板内调用方法时传递参数. 数据应当在视图中计算, 然后再传递给模板.

自定义标签和过滤器库P

Certain applications provide custom tag and filter libraries. To access them in a template, ensure the application is in INSTALLED_APPS (we'd add 'django.contrib.humanize' for this example), and then use the load tag in a template:

{% load humanize %}

{{ 45000|intcomma }}

In the above, the load tag loads the humanize tag library, which then makes the intcomma filter available for use. If you've enabled django.contrib.admindocs, you can consult the documentation area in your admin to find the list of custom libraries in your installation.

The load tag can take multiple library names, separated by spaces. Example:

{% load humanize i18n %}

请参考 自定义模板(template)的标签(tags)和过滤器(filters) 以获得更多的有关自定义标签的内容.

自定义库和模板继承P

当你加载一个自定义标签和过滤器库, 这些被加载的标签/过滤器只能在当前模板中使用 -- 其他与此模板有继承关系的(父/子)模板均不能使用这些标签/过滤器.

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. The child template is responsible for its own {% load humanize %}.

这是因为这能使模板更健全且更好维护.

参见

The Templates Reference
Covers built-in tags, built-in filters, using an alternative template, language, and more.