本文将介绍Django模版系统的语法。若是您须要更多该系统如何工做的技术细节,以及但愿扩展它,请浏览 _The Django template language: for Python programmers_.css
Django模版语言的设计致力于在性能和简单上取得平衡。
它的设计使习惯于使用HTML的人也可以自如应对。若是您有过使用其余模版语言的经验,像是 Smarty 或者 Jinja2, 那么您将对Django的模版语言感到一见如故。html
理念python
若是您有过编程背景,或者您使用过一些在HTML中直接混入程序代码的语言,那么如今您须要记住,Django的模版系统并非简单的将Python嵌入到HTML中。
设计决定了:模版系统致力于表达外观,而不是程序逻辑。数据库
Django的模版系统提供了和一些程序结构功能相似的标签——用于布尔判断的 if
标签, 用于循环的 for
标签等等。——可是这些都不是简单的做为Python代码那样来执行的,而且,模版系统也不会随意执行Python表达式。只有下面列表中的标签、过滤器和语法才是默认就被支持的。 (可是您也能够根据须要添加 _ 您本身的扩展 _ 到模版语言中)。django
模版是纯文本文件。它能够产生任何基于文本的的格式(HTML,XML,CSV等等)。编程
模版包括在使用时会被值替换掉的 变量,和控制模版逻辑的 标签。api
下面是一个小模版,它说明了一些基本的元素。后面的文档中会解释每一个元素。数组
{% 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模版。在线上世界,咱们在email、Javascript和CSV中使用它。你能够在任何基于文本的格式中使用这个模版语言。安全
还有,让人类编辑HTML简直是施虐狂的作法!
变量看起来就像是这样: {{ variable }}
. 当模版引擎遇到一个变量,它将计算这个变量,而后用结果替换掉它自己。变量的命名包括任何字母数字以及下划线 ("_"
)的组合。点("."
) 也在会变量部分中出现,不过它有特殊的含义,咱们将在后面说明。重要的是, _你不能在变量名称中使用空格和标点符号。_
使用点 (.
) 来访问变量的属性。
幕后
从技术上来讲,当模版系统遇到点,它将以这样的顺序查询:
字典查询(Dictionary lookup)
属性或方法查询(Attribute or method lookup)
数字索引查询(Numeric index lookup)
若是计算结果的值是可调用的,它将被无参数的调用。调用的结果将成为模版的值。
这个查询顺序,会在优先于字典查询的对象上形成意想不到的行为。例如,思考下面的代码片断,它尝试循环 collections.defaultdict
:
{% for k, v in defaultdict.iteritems %} Do something with k and v here... {% endfor %}
由于字典查询首先发生,行为奏效了而且提供了一个默认值,而不是使用咱们指望的 .iteritems()
方法。在这种状况下,考虑首先转换成字典。
在前文的例子中, {{ section.title }}
将被替换为 section
对象的 title
属性。
若是你使用的变量不存在, 模版系统将插入 string_if_invalid
选项的值, 它被默认设置为''
(空字符串) 。
注意模版表达式中的“bar”, 好比 {{ foo.bar }}
将被逐字直译为一个字符串,而不是使用变量“bar”的值,若是这样一个变量在模版上下文中存在的话。
您能够经过使用 过滤器来改变变量的显示。
过滤器看起来是这样的:{{ name|lower }}
。这将在变量 {{ name }}
被过滤器 lower
过滤后再显示它的值,该过滤器将文本转换成小写。使用管道符号 (|
)来应用过滤器。
过滤器可以被“串联”。一个过滤器的输出将被应用到下一个。{{ text|escape|linebreaks }}
就是一个经常使用的过滤器链,它编码文本内容,而后把行打破转成<p>
标签。
一些过滤器带有参数。过滤器的参数看起来像是这样: {{ bio|truncatewords:30 }}
。这将显示 bio
变量的前30个词。
过滤器参数包含空格的话,必须被引号包起来;例如,链接一个有逗号和空格的列表,你须要使用 {{ list|join:", " }}
。
Django提供了大约六十个内置的模版过滤器。你能够在 _内置过滤器参考手册_中阅读所有关于它们的信息。为了体验一下它们的做用,这里有一些经常使用的模版过滤器:
若是一个变量是false或者为空,使用给定的默认值。不然,使用变量的值。例如:
{{ value|default:"nothing" }}
若是 value
没有被提供,或者为空, 上面的例子将显示“nothing
”。
返回值的长度。它对字符串和列表都起做用。例如:
{{ value|length }}
若是 value
是 ['a', 'b', 'c', 'd']
,那么输出是 4
。
将值格式化为一个 “人类可读的” 文件尺寸 (例如 '13 KB'
, '4.1 MB'
, '102 bytes'
, 等等)。例如:
{{ value|filesizeformat }}
若是 value
是 123456789,输出将会是 117.7 MB
。
再说一下,这仅仅是一些例子;查看 _内置过滤器参考手册_ 来获取完整的列表。
您也能够建立本身的自定义模版过滤器;参考 _自定义模版标签和过滤器_。
更多
Django’s admin interface can include a complete reference of all template tags and filters available for a given site. See _The Django admin documentation generator_.
标签看起来像是这样的: {% tag %}
。标签比变量更加复杂:一些在输出中建立文本,一些经过循环或逻辑来控制流程,一些加载其后的变量将使用到的额外信息到模版中。
一些标签须要开始和结束标签 (例如{% tag %} ...
标签 内容 ... {% endtag %})。
Django自带了大约24个内置的模版标签。你能够在 _内置标签参考手册_中阅读所有关于它们的内容。为了体验一下它们的做用,这里有一些经常使用的标签:
循环数组中的每一个元素。例如,显示 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 %}
在上面的例子中,若是 athlete_list
不是空的,运动员的数量将显示为 {{ athlete_list|length }}
的输出。另外一方面, 若是 athlete_in_locker_room_list
不为空, 将显示 “Athletes should be out...” 这个消息。若是两个列表都是空的,将显示 “No athletes.” 。
您也能够在if
标签中使用过滤器和多种运算符:
{% if athlete_list|length > 1 %} Team: {% for athlete in athlete_list %} ... {% endfor %} {% else %} Athlete: {{ athlete_list.0.name }} {% endif %}
当上面的例子工做时,须要注意,大多数模版过滤器返回字符串,因此使用过滤器作数学的比较一般都不会像您指望的那样工做。length
是一个例外。
Set up template inheritance (see below), a powerful way
of cutting down on “boilerplate” in templates.
再说一下,上面的仅仅是整个列表的一部分;查看 _内置标签参考手册_ 来获取完整的列表。
您也能够建立您本身的自定义模版标签;参考 _自定义模版标签和过滤器_。
更多
Django’s admin interface can include a complete reference of all template tags and filters available for a given site. See _The Django admin documentation generator_.
要注释模版中一行的部份内容,使用注释语法 {# #}
.
例如,这个模版将被渲染为 'hello'
:
{# greeting #}hello
注释能够包含任何模版代码,有效的或者无效的均可以。例如:
{# {% if foo %}bar{% else %} #}
这个语法只能被用于单行注释 (在 {#
和 #}
分隔符中,不容许有新行)。若是你须要注释掉模版中的多行内容,请查看 comment
标签。
Django模版引擎中最强大也是最复杂的部分就是模版继承了。模版继承可让您建立一个基本的“骨架”模版,它包含您站点中的所有元素,而且能够定义可以被子模版覆盖的 blocks 。
经过从下面这个例子开始,能够容易的理解模版继承:
<!DOCTYPE html> <html lang="en"> <head> <link rel="stylesheet" href="style.css" /> <title>{% block title %}My amazing site{%/span> 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骨架。“子模版”的工做是用它们的内容填充空的blocks。
在这个例子中, block
标签订义了三个能够被子模版内容填充的block。 block
告诉模版引擎: 子模版可能会覆盖掉模版中的这些位置。
子模版可能看起来是这样的:
{% extends "base.html" %}/span> {% 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
标签,并用子模版中的内容来替换这些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,因此系统使用了父模版中的值。父模版的 {% block %}
标签中的内容老是被用做备选内容(fallback)。
您能够根据须要使用多级继承。使用继承的一个经常使用方式是相似下面的三级结构:
建立一个 base.html
模版来控制您整个站点的主要视觉和体验。
为您的站点的每个“部分”建立一个base_SECTIONNAME.html
模版。 例如, base_news.html
, base_sports.html
。这些模版都继承自 base.html
,而且包含了每部分特有的样式和设计。
为每一种页面类型建立独立的模版,例如新闻内容或者博客文章。这些模版继承了有关的部分模版(section template)。
这种方式使代码获得最大程度的复用,而且使得添加内容到共享的内容区域更加简单,例如,部分范围内的导航。
这里是使用继承的一些提示:
若是你在模版中使用 {% extends %}
标签,它必须是模版中的第一个标签。其余的任何状况下,模版继承都将没法工做。
在base模版中设置越多的 {% block %}
标签越好。请记住,子模版没必要定义所有父模版中的blocks,因此,你能够在大多数blocks中填充合理的默认内容,而后,只定义你须要的那一个。多一点钩子总比少一点好。
若是你发现你本身在大量的模版中复制内容,那可能意味着你应该把内容移动到父模版中的一个 {% block %}
中。
If you need to get the content of the block from the parent template, the {{ block.super }}
variable will do the trick. This is useful if you want to add to the contents of a parent block instead of completely overriding it. Data inserted using {{ block.super }}
will not be automatically escaped (see the next section), since it was already escaped, if necessary, in the parent template.
为了更好的可读性,你也能够给你的 {% endblock %}
标签一个 名字 。例如:
{% block content %} ... {% endblock content %}
在大型模版中,这个方法帮你清楚的看到哪个 `{% block %}` 标签被关闭了。
最后,请注意您并不能在一个模版中定义多个相同名字的 block
标签。这个限制的存在是由于block标签的做用是“双向”的。这个意思是,block标签不只提供了一个坑去填,它还在 _父模版_中定义了填坑的内容。若是在一个模版中有两个名字同样的 block
标签,模版的父模版将不知道使用哪一个block的内容。
当从模版中生成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字符转换成无害的。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. It’s easy to forget to escape data.
第二,你能够利用Django的自动HTML转义。 本节描述其他部分描述的是自动转义是如何工做的
By default in Django, every template automatically escapes the output of every variable tag. Specifically, these five characters are escaped:
<
会转换为<
>
会转换为>
'
(单引号) 会转换为'
"
(双引号)会转换为 "
&
会转换为 &
咱们要再次强调这是默认行为。若是你使用Django的模板系统,会处于保护之下。
若是你不但愿数据自动转义,在站点、模板或者变量级别,你可使用几种方法来关闭它。
然而你为何想要关闭它呢?因为有时,模板变量含有一些你_打算_渲染成原始HTML的数据,你并不想转义这些内容。例如,你可能会在数据库中储存一些HTML代码,而且直接在模板中嵌入它们。或者,你可能使用Django的模板系统来生成_不是_HTML的文本 -- 好比邮件信息。
使用safe
过滤器来关闭独立变量上的自动转移:
This will be escaped: {{ data }} This will not be escaped: {{ data|safe }}
_safe_是_safe from further escaping_或者_can be safely interpreted as HTML_的缩写。
在这个例子中,若是data
含有'<b>'
,输出会是:
This will be escaped: <b> This will not be escaped: <b>
要控制模板上的自动转移,将模板(或者模板中的特定区域)包裹在autoescape
标签 中,像这样:
{% autoescape off %} Hello {{ name }} {% endautoescape %}
autoescape
标签接受on
或者 off
做为它的参数。有时你可能想在自动转移关闭的状况下强制使用它。下面是一个模板的示例:
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
标签包含的模板,就像全部block标签那样。例如:
base.html
{% autoescape off %} <h1>{% block title %}{% endblock %}</h1> {% block content %} {% endblock %} {% endautoescape %}
child.html
{% extends "base.html" %} {% block title %}This & that{% endblock %} {% block content %}{{ greeting }}{% endblock %}
因为自动转义标签在base模板中关闭,它也会在child模板中关闭,致使当 greeting
变量含有<b>Hello!</b>
字符串时,会渲染HTML。
<h1>This & that</h1> <b>Hello!</b>
一般,模板的做用并不很是担忧自动转义。Python一边的开发者(编写视图和自定义过滤器的人)须要考虑数据不该被转移的状况,以及合理地标记数据,让这些东西在模板中正常工做。
若是你建立了一个模板,它可能用于你不肯定自动转移是否开启的环境,那么应该向任何须要转移的变量添加 escape
过滤器。当自动转移打开时,escape
过滤器_双重过滤_数据没有任何危险 -- escape
过滤器并不影响自动转义的变量。
像咱们以前提到的那样,过滤器参数能够是字符串:
{{ data|default:"This is a string literal." }}
全部字面值字符串在插入模板时都 不会带有任何自动转义 -- 它们的行为相似于经过 safe
过滤器传递。背后的缘由是,模板做者能够控制字符串字面值得内容,因此它们能够确保在模板编写时文本通过正确转义。
也便是说你能够编写
{{ data|default:"3 < 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 %}
与之相似,_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
,你能够查询admin站点中的文档部分,来寻找你的安装中的自定义库列表。
load
标签能够接受多个库名称,由空格分隔。例如:
{% load humanize i18n %}
关于编写你本身的自定义模板库,详见_自定义模板标签和过滤器_。
当你加载一个自定义标签或过滤器库时,标签或过滤器只在当前模板中有效 -- 并非带有模板继承关系的任何父模板或者子模版中都有效。
例如,若是一个模板foo.html
带有{% load humanize %}
,子模版(例如,带有{% extends "foo.html" %}
)中_不能_ 访问humanize模板标签和过滤器。子模版须要添加本身的 {% load humanize %}
。
这个特性是可维护性和逻辑性的缘故。
另见
Covers built-in tags, built-in filters, using an alternative template,
language, and more.
译者:Django 文档协做翻译小组,原文:Language overview。
本文以 CC BY-NC-SA 3.0 协议发布,转载请保留做者署名和文章出处。
Django 文档协做翻译小组人手紧缺,有兴趣的朋友能够加入咱们,彻底公益性质。交流群:467338606。