本文將介紹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。