Django模板语言(译)
原文地址:https://docs.djangoproject.com/zh-hans/2.1/ref/templates/language/
翻譯日期:2019年3月8日-2019年3月9日
by:桂浮云(laurelclouds@163.com)
轉載請保留以上,謝謝
本文檔描述了Django模板系統的語法。如果您找的是模板語言的工作機制和功能擴展等方面的更多技術觀點,請參閱:Django模板語言:程序員適用 部分。
Django模板語言的設計目標是平衡功能性和易用性,讓習慣HTML的人更容易接受。如果您接觸過其他文本類模板語言,例如Smarty或Jinja2,那么應該更容易認同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 }}。當模板引擎發現模板變量的時候,就會計算變量variable的值并將模板變量替換為求值結果。變量名由任意英文字母、數字和下劃線組合而成,但首字母不可以是下劃線。英文的句號點(.)也會出現在變量部分,盡管據點是有特殊含義的(后面有具體說明)(譯者注:但仍然可以將之與前后的部分作為整體視為一個模板變量)。重要的是:變量名之中不能含有空格或標點符號。
英文句點(.)是用來訪問變量屬性的。
考慮以下場景技術上,當模板系統碰到英文句點(.)時,將按以下查表順序進行求值:
- 字典查表
- 屬性或方法查表
- 數字索引查表
以上查表次序意味著,重寫了字典類的對象實例可能會導致不可測的情況發生。例如,想象以下嘗試基于collections.defaultdict循環的代碼段: {% for k, v in defaultdict.items %}Do something with k and v here... {% endfor %}
?
由于模板系統首先通過字典查表,因此將通過字典查表操作提供一個默認值,而不是預期的(defaultdict對象實例的).items方法。這種情況下,可以視同把defaultdict對象首先轉換為了dictionary對象。(本段可能存在理解錯誤,敬請批評指正)?
以上示例中,{{ section.title }} 將被section對象的title屬性所替換。
如果使用了不存在的變量,模板系統將插入string_if_invalid選項的值——也即默認為''(空字符串)。
注意:模板上下文里有bar存在,類似{{ foo.bar }}的模板表達式中的bar,將被解釋為一個字面的字符串,而不是bar變量的值
以下劃線開頭的變量屬性將不能訪問,因為通常這種屬性被視為私有屬性。
?
過濾器
?
過濾器用來改變變量的顯示方式。
過濾器是{{ name|lower }}這樣的,模板變量{{ name }}的值如何顯示,是通過其后的lower過濾器確定的,lower過濾器實現文本轉換為小寫字母的轉換。其中的豎線管道符號(|)表示啟用一個過濾器。
“鏈式”過濾是可以的,也即一個過濾器的輸出可以作為下一個過濾器的輸入。{{ text|escape|linebreaks }} 是一種常見的習慣用法,用于轉移文本內容,然后將換行符轉換為<p>標記。
有些過濾器是帶參數的。例如:{{ bio|truncatewords:30 }},表示顯示bio變量的前30個單詞。
過濾器參數如果包含空格的話,必須用引號括起來。如,將一個列表的內容用逗號(,)和空格連接在一起,可以這樣做:{{ list|join:", " }}。
django內置了大約60個過濾器。所有過濾器可參見內置過濾器參考。等不及要嘗嘗鮮的話,這里有一些更為常用的模板過濾器:
default
如果一個變量為false或空,則使用給定的默認值。否則,使用變量值。如:
{{ value|default:"nothing" }}?
如果value變量沒有提供或者為空,上面的代碼將顯示“nothing”。
length
返回值的長度。用于字符串和列表list。如:
{{ 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 %} ……標記內容……{% endtag %})。
Django附帶了大約24個內置模板標記。您可以在內置標記參考中閱讀所有相關內容。嘗鮮需要,以下是一些較為常用的標記:
for
迭代循環訪問數組中的每一項。例如,列表顯示athlete_list中的athetes:
<ul> {% for athlete in athlete_list %}<li>{{ athlete.name }}</li> {% endfor %} </ul>?
if,elif及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 %}?
以上,如果althlete_list非空,通過{{ athlete_list|length }}就可以顯示出althlete_list中包含的athlete的個數。否則,如果athlete_in_locker_room_list非空,將顯示“Athletes should be ...”的消息內容。如果athlete_list和athlete_in_locker_room_list兩個變量都為空,顯示內容為“No athletes”。
過濾器和各類操作符也可以用于if標記之中:
{% if athlete_list|length > 1 %}Team: {% for athlete in athlete_list %} ... {% endfor %} {% else %}Athlete: {{ athlete_list.0.name }} {% endif %}盡管以上代碼有效,但要注意大多數模板過濾器返回值為字符串,因此,使用過濾器時進行數學比較通常無法按預期那樣正常運行。length過濾器是個例外。
block和extends
設置模板繼承(見下),是一種減少模板“樣板”的有力方式。
再次聲明,以上只是內置標記中的一小部分標記;完整列表參見內置標記參考。
同時,我們還可以創建自定義模板標記,參見自定義模板的標簽和過濾器
參見 django管理接口可以包含指定站點所有可用的模板標記和過濾器的一個完整引用,參見django管理文檔生成器。?
注釋
模板中注釋行內部分內容,其語法為:{# #}。
例如,下面的模板繪制內容為“hello”。
{# greeting #}hello?
注釋內可以包含任意的模板代碼,無論有效與否。例如:
{# {% if foo %}bar{% else %} #}?
該語法僅用于單行注釋({#和#}之間不允許換行)。如果需要注釋多行模板內容,可參見comment標記。
?
模板繼承
最最強有力以及最最復雜的django模板引擎部分就是模板繼承。模板繼承允許您構建一個基礎“骨架”模板,其中包含站點的所有常用元素,并通過定義block,以便于子模板覆蓋這些block的內容。
要理解模板繼承,最簡單的辦法是拿范例說話:
<!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塊,因此生成的結果使用父模板定義的siderbar塊來求值。父模板{% block %}標記中的內容將始終作為后備內容使用(以防不時之需)。
可以如需定制多層繼承。通用的方法為以下三層的方案:
- 創建一個base.html模板,以滿足站點主要的觀感需要;
- 為每個“片段”,創建一個base_片段名.html模板;比如:base_news.html,base_sports.html。這些模板全部繼承于(擴展自)base.html,并包含片段具體的風格和設計。
- 為每種頁面類型創建單獨的模板,比如新聞文章或者博客條目。這些模板繼承于(擴展自)有關片段模板。
該方案最大化地復用代碼,并簡化了添加項目到共享內容區域的工作,比如說,片段范圍內的導航
關于繼承,這里還有以下提示:
- 如果模板中使用{% extends %}標記,那么該標記必須為當前模板中的第一個標記。否則,模板繼承不能運行。
- 基礎模板(如base.html)中{% block %}標記越多越好。記住,子模板不必定義所有父模板中的塊,因此在一些塊中填充默認內容是明智的,如有必要再在子模板中定義這些塊。多比少好,有備無患。
- 如果發現一些模板中復制了相同內容,可能意味著應該將這些內容移動到父模板的一個{% blcok %}塊中。
- 如果想使用父模板定義的塊內容,可采用{{ block.super }}實現。這對于想使用父模板塊內容但并不是完全覆蓋它的時候非常有用。以{{ block.super }}插入的數據不會自動轉義(見下節),因為數據已經轉義過;如確有必要,請在父模板中修改實現。
- {% block %}塊外以as標記定義的變量不能再塊內使用。例如:下面的模板啥也沒繪制:
?
- 為了便于閱讀,{% endblock %}標記中可以選擇是否加注塊名。例子:
?
在較大的模板中,這樣做有助于明白結束的是對應的哪一個{% block %}標記。
最后,提醒注意,不要在同一模板中定義多個同名的block標記。該限制適用于塊標記以“雙向”模式運行,也即,塊標記不是僅僅(由父模板)挖了個坑備(子模板同名塊)填——也定義了父模板同名塊用于填坑時用到的(子模塊)內容。如果一個模板中存在兩個同名的塊標記,父模板就會搞不清究竟該使用哪個塊的內容。
?
HTML自動轉義
模板產出HTML時,總會存在字符串變量對結果HTML的影響風險,比如說,考慮以下模板代碼:
Hello, {{ name }}?
起初,看起來就是用一種無害的方式顯示用戶名而已,但你認為如果用戶使用下面的代碼作為名字時將發生什么:
<script>alert('hello')</script>?
以此作為name變量的值時,模板得出的結果為:
Hello, <script>alert('hello')</script>?
呃……,這意味著瀏覽器將彈出JavaScript alert對話框!
與之類似,如果名字里包含<符號,如下:
<b>username?
模板繪制結果為:
Hello, <b>username?
……,對網頁剩余部分的內容將會統統字體加粗!
顯然,用戶提交的數據不應盲目相信并直接插入到您的網頁中,因為惡意用戶可能會利用這種漏洞來做壞事。此類安全漏洞稱為 跨站點腳本(XSS)攻擊。
要避免此類問題,有兩個選項:
- 一是確保每個不確信變量都通過escape過濾器(見下)進行轉義,從而讓某些有害HTML字符無害化。對django來說,最初的幾年里默認采用就是這種解決方案。但問題是這種解決方案把問題推給了開發者/模板編寫者,由他們負責轉義。但轉義數據本來就是一件容易忘記去做的事兒。
- 二是采用django的自動HTML轉義。本節以下部分將討論自動轉義的運行機制。
django默認將每個模板中的每個變量標記的輸出自動轉義。具體而言,將自動轉義以下5個字符:
- < 轉換為<
- > 轉換為>
- '(單引號) 轉換為'
- "(雙引號) 轉換為"
- & 轉換為 &
再次說明,我們強調這種轉義操作時默認打開。只要用的是django模板系統,就是被保護的。
?
如何關閉自動轉義
如果您不想讓數據自動轉義,在每個站點、模板或變量層級上,有幾個不同的關閉方法。
為啥要關閉?因為有些時候我們的目的就是將模板變量直接生成原始的HTML,這種情況下,當然不希望內容被轉義。例如:可能在數據庫中保存了一大塊HTML數據,并希望將它直接內置在模板之中;又或者,你用django模板系統輸出的文本并不是HTML文本——如郵件消息等等。
>>針對單獨變量而言
關閉某個單獨變量的自動轉義,可以使用safe過濾器:
?
This will be escaped: {{ data }} This will not be escaped: {{ data|safe }}?
確信變量直接解釋為HTML無害,又可以保證將來的變量仍然可以自動轉義,這個時候就可以使用safe過濾器。該例中,如果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標記加載的模板,就像對塊標記的影響一樣。例如:
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.html模板中自動轉義已經關閉,子模板中也會是關閉狀態。當greeting變量值為“<b>Hello!</b>”時,將輸出如下結果:
<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”語法,為此,如果一個包含task外鍵的comment模型,可用以下方式循環遍歷一個給定task的所有comment:
{% for comment in task.comment_set.all %}{{ comment }} {% endfor %}?
? 同樣的,QuerySet提供了count()方法,用于統計所包含對象的個數。因此,計算當前task相關的所有comment的個數,可以這樣做:
{{ task.comment_set.all.count }}并且,還可以簡便訪問模型中明確自定義的方法:
models.py
class Task(models.Model):def foo(self):return "bar"?
template.html
{{ task.foo }}由于Django故意限制了模板語言中可用的邏輯處理量,所以在模板中傳遞參數到調用方法時不可能的。數據應該在views中進行計算,然后再傳值給模板進行顯示。
?
自定義標記和過濾器庫
某些應用提供自定義標記和過濾器庫。如需訪問,則首先要確定應用在INSTALLED_APPS內(例如,我們要添加了'django.contrib.humanize'),然后使用load標記進行加載:
{% load humanize %}{{ 45000|intcomma }}?
如上,load標記加載了humanize標記庫,以便使用其中的intcomma過濾器。如果啟用了django.contrib.admindocs,你可在安裝的自定義庫列表中查看管理文檔區域。
load標記可帶多個庫名,以空格分隔。如:
{% load humanize i18n %}?
參見:自定義模板(template)的標簽(tags)和過濾器(filters)。編寫自定義模板庫時可以參考下。
?
自定義庫和模板繼承
加載自定義標記或過濾庫時,標記/過濾器僅對當前模板可用——不包括模板繼承路徑中的任何父或子模板。
例如,如果foo.html模板包含{% load humanize %},子模板(包含{% extends "foo.html" %}不能訪問humanize的模板標記和過濾器。子模板有責任自行加載自己的{% load humanize %}。
這是一個基于可維護性和符合常理目的的特性。
?
參見模板參考 包括內置標記,內置過濾器,另類模板、語言的使用等等?
轉載于:https://www.cnblogs.com/laurelclouds/p/10497705.html
與50位技術專家面對面20年技術見證,附贈技術全景圖總結
以上是生活随笔為你收集整理的Django模板语言(译)的全部內容,希望文章能夠幫你解決所遇到的問題。