1. 程式人生 > >Django 2.1.3 文件-模板-語言概述

Django 2.1.3 文件-模板-語言概述

模板語言概述


本文件解釋了Django模板系統的語言語法。如果您正在尋找關於它如何工作以及如何擴充套件它的更多技術觀點,請參閱
Django模板語言:適用於Python程式設計師

Django的模板語言旨在在功能和輕鬆之間取得平衡。它旨在讓那些習慣使用HTML的人感到舒服。如果您對其他基於文字的模板語言(如Smarty 或Jinja2)有任何接觸,那麼您應該對Django的模板感到親切。

信條
如果您有程式設計背景,或者習慣於將程式設計程式碼直接混合到HTML中的語言,那麼您需要記住,Django模板系統不僅僅是嵌入到HTML中的Python。這是設計的:模板系統用於表示,而不是程式邏輯。

Django模板系統提供的標籤功能類似於一些程式設計結構 - if標籤,for 標籤,迴圈標籤等 - 但這些不是簡單地作為相應的Python程式碼執行,模板系統不會執行任意Python表示式。預設情況下,僅支援下面列出的標籤,過濾器和語法(儘管您可以根據需要將自己的擴充套件新增到模板語言中)。↑

1. 模板

模板只是一個文字檔案。它可以生成任何基於文字的格式(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模板。在World Online,我們將其用於電子郵件,JavaScript和CSV。您可以將模板語言用於任何基於文字的格式。
哦,還有一件事:讓人類編輯XML很變態!↑

2. 變數

變數看起來像這樣:{{ variable }}。當模板引擎遇到變數時,它會計算該變數並將其替換為結果。變數名由字母,數字字元和下劃線(_)的任意組合組成,但不能以下劃線開頭。點(.)也出現在可變部分中,儘管它具有特殊含義,如下所示。重要的是,變數名稱中不能包含空格或標點符號。

使用點(.)訪問變數的屬性。


在幕後

從技術上講,當模板系統遇到一個點時,它會按以下順序嘗試以下查詢:

  • 字典查詢
  • 屬性或方法查詢
  • 數字索引查詢

如果結果值是可呼叫的,則呼叫它時不帶引數。呼叫的結果成為模板值。

此查詢順序可能會導致覆蓋字典查詢的物件出現一些意外行為。例如,請考慮以下程式碼片段嘗試遍歷collections.defaultdict

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

因為字典查詢首先發生,所以該行為啟動並提供預設值而不是使用預期的.items()方法。在這種情況下,請考慮先轉換為字典。↑


在上面的例子中,{{ section.title }}將替換為title物件的section屬性。

如果使用不存在的變數,模板系統將插入string_if_invalid選項的值,’'預設情況下設定為(空字串)。

請注意,模板表示式{{ foo.bar }}中的“bar” 將被解釋為文字字串,而不使用變數“bar”的值(如果模板上下文中存在)。

可能無法訪問以下劃線開頭的變數屬性,因為它們通常被視為私有
在這裡插入圖片描述

3. 過濾器

您可以使用過濾器修改要顯示的變數。

過濾器看起來像這樣:{{ name|lower }}。這會在通過lower過濾器過濾後顯示變數{{ name }}的值, 過濾器會將文字轉換為小寫。使用豎線(|)來應用過濾器。

過濾器可以“連結”多次。一個過濾器的輸出應用於下一個過濾器。 {{ text|escape|linebreaks }}是轉義文字內容,然後將換行符轉換為<p>標記的常用習慣用法。

一些過濾器採用引數。過濾器引數如下所示:{{ bio|truncatewords:30 }}。這將顯示變數bio的前30個單詞。

過濾器引數如果使用空格,必須用引號引起。 例如,使用逗號和空格加入列表的過濾器:{{ list|join:", " }}

Django提供了大約60個內建模板過濾器。您可以在 內建過濾器 參考中閱讀所有相關內容。為了讓您瞭解可用的內容,以下是一些常用的模板過濾器:

default
如果變數為false或為空,請使用給定的預設值。否則,使用變數的值。例如:
{{ value|default:"nothing" }}
如果value未提供或為空,則上面將顯示“ nothing”。

length
返回值的長度。這適用於字串和列表。例如:
{{ value|length }}
如果value是,輸出將是。[‘a’, ‘b’, ‘c’, ‘d’]4

filesizeformat
格式,如一個“人類可讀”的檔案大小的值(即,13 KB ,4.1 MB ,102 bytes’等等)。例如:
{{ value|filesizeformat }}
如果value是123456789,則輸出為117.7 MB。

同樣,這些僅僅是幾個例子;
您還可以建立自己的自定義模板過濾器; 見 自定義模板的標籤(tags)和過濾器(filters)

參見
Django的管理介面包含對給定站點可用的所有模板標籤和過濾器的完整參考。請參閱 Django管理文件生成器。↑

4. 標籤

標籤看起來像這樣:{% tag %}。標籤比變數更復雜:有些在輸出中建立文字,有些通過執行迴圈或邏輯來控制流,有些則將外部資訊載入到模板中以供後來的變數使用。

某些標籤需要開始和結束標籤(即{% tag %} ... tag contents ... {% endtag %})。

Django附帶了大約24個內建模板標籤。您可以在內建標籤引用中閱讀所有相關內容。為了讓您瞭解可用的內容,以下是一些更常用的標記:

for
迴圈遍歷陣列中的每個專案。例如,要顯示athlete_list提供的運動員列表:

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

在上面,如果athlete_list不是空的,運動員的數量將由變數{{ athlete_list|length }}顯示。否則,如果athlete_in_locker_room_list不為空,將顯示訊息Athletes should be out of the locker room soon!。如果兩個名單都是空的,“” 將顯示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
設定模板繼承(見下文),這是一種在模板中減少“重複程式碼”的強大方法。
同樣,以上只是整個列表的一點; 請參閱完整列表的內建標籤

您還可以建立自己的自定義模板標籤; 見 自定義模板的標籤(tags)和過濾器(filters)

參見
Django的管理介面包含對給定站點可用的所有模板標籤和過濾器的完整參考。請參閱 Django管理文件生成器。↑

5. 註釋

兩種註釋:單行註釋({# ... #})和多行註釋{% comment %}...{% endcomment %}

{# hello 單行註釋 #}

{% comment %}
多行註釋1
多行註釋2 
...
{% endcomment %}

6. 模板繼承

Django模板引擎中最強大的 - 也是最複雜的 - 是模板繼承。模板繼承允許您構建一個基礎“框架”模板,其中包含站點的所有常見元素,並定義子模板可以覆蓋的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 block,因此將使用父模板中的值。 父模板中的{% block %}標記內的內容始終用作後備。

您可以根據需要使用盡可能多的繼承級別。使用繼承的一種常見方法是以下三級方法:

  • 建立一個base.html模板,其中包含您網站的主要外觀。
  • 為您網站的每個“部分” 建立一個base_SECTIONNAME.html模板。例如base_news.htmlbase_sports.html。這些模板都擴充套件base.html幷包含特定於部分的樣式/設計。
  • 為每種型別的頁面建立單獨的模板,例如新聞文章或部落格條目。這些模板擴充套件了相應的部分模板。

這種方法可以最大化程式碼重用,並且可以輕鬆地將專案新增到共享內容區域,例如部分範圍的導航。

以下是使用繼承的一些提示:

  • 如果在模板中使用{% extends %},則它必須是該模板中的第一個模板標記。否則,模板繼承將不起作用。

  • 基礎模板中最好有很多{% block %}標籤。請記住,子模板不必定義所有父塊,因此您可以在多個塊中填寫合理的預設值,然後僅定義稍後需要的塊。最好有更多的鉤子而不是更少的鉤子。

  • 如果您發現自己在許多模板中複製了內容,則可能意味著您應該將內容移動到父模板中的{% block %}內。

  • 如果您需要從父模板獲取塊的內容,該變數{{ block.super }}將起作用。如果要新增父塊的內容而不是完全覆蓋它,這將非常有用。使用的{{ block.super }}的資料不會自動轉義(請參閱下一節),因為必要時,它已在父模板中轉義。

  • 使用模板標籤語法在{% block %}之外建立的變數不能在塊內使用。例如,此模板不呈現任何內容:
    譯者注:注意變數是用模板標籤的語法建立的,而不是你從檢視傳遞過來的變數哦!

{% trans "Title" as title %}
{% block content %}{{ title }}{% endblock %}
  • 對於額外的可讀性,您可以給你的{% endblock %}選擇給一個名字例如:
{% block content %}
...
{% endblock content %}

在較大的模板中,此技術可幫助您檢視 正在關閉的{% block %}標籤。

最後,請注意,您無法在同一模板中定義多個具有相同名稱的block標籤。存在這種限制是因為block標籤在“兩個”方向上工作。也就是說,block標籤不僅提供填充孔 - 它還定義填充父級孔的內容。如果block模板中有兩個類似命名的標記,則該模板的父級將不知道要使用哪個塊的內容。

7. 自動HTML轉義

從模板生成HTML時,變數將始終存在影響生成的HTML的字元的風險。例如,考慮這個模板片段:

Hello, {{ name }}

起初,這似乎是一種顯示使用者名稱稱的無害方式,但考慮如果使用者輸入其名稱為<script>alert('hello')</script>,會發生什麼?

使用此名稱值,模板將呈現為:

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

…這意味著瀏覽器會彈出一個JavaScript警告框!

同樣,如果名稱包含’<'符號,如<b>username
這將導致像這樣的渲染模板:

Hello, <b>username

反過來,這將導致網頁的其餘部分被加粗!

顯然,使用者提交的資料不應盲目信任並直接插入到您的網頁中,因為惡意使用者可能會利用這種漏洞來做壞事。此類安全漏洞稱為 跨站點指令碼(XSS)攻擊。

要避免此問題,您有兩種選擇:

  • 一,您可以確保通過escape過濾器(下面會講)執行每個不受信任的變數 ,這會將可能有害的HTML字元轉換為無害的HTML字元。這是Django最初幾年的預設解決方案,但問題在於它讓你(開發人員/模板作者)有責任確保你轉義一切。很容易忘記轉義資料。
  • 二,你可以利用Django的自動HTML轉義功能。本節的其餘部分將介紹自動轉義的工作原理。

預設情況下,Django中的每個模板都會自動轉義每個變數標記的輸出。具體來說,這五個字元被轉義:

  • < 轉換為&lt;
  • > 轉換為 &gt;
  • ' (單引號)轉換為&#39;
  • " (雙引號)轉換為 &quot;
  • & 轉換為 &amp;

同樣,我們強調預設情況下此行為已啟用。如果您正在使用Django的模板系統,那麼您將受到保護。

7.1 如何關閉它

如果您不希望在每個站點,每個模板級別或每個變數級別上自動轉義資料,則可以通過多種方式將其關閉。

你為什麼要把它關掉?因為有時,模板變數包含您打算作為原始HTML呈現的資料,在這種情況下,您不希望對其內容進行轉義。例如,您可以在資料庫中儲存一團HTML,並希望將其直接嵌入到模板中。或者,您可能正在使用Django的模板系統來生成非 HTML的文字- 例如,電子郵件訊息。

7.1.1 對於個別變數

要禁用單個變數的自動轉義,請使用safe 過濾器:
例如,在檢視中sv='<h1>sv</h1>',在模板中使用如下:

{{ sv }}
{{ sv|safe }}

瀏覽器的效果:
在這裡插入圖片描述
對應的html原始檔如下:

&lt;h1&gt;sv&lt;/h1&gt;
<h1>sv</h1>

7.1.2 對於模板塊

要控制模板的自動轉義,請在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 &amp; that{% endblock %}
{% block content %}{{ greeting }}{% endblock %}

由於在基本模板中關閉了自動轉義,因此它也將在子模板中關閉,從而在greeting變數包含字串HTML <b>Hello!</b>時生成以下呈現的內容:
瀏覽器:
在這裡插入圖片描述
html中的內容:

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

7.2 注意

通常,模板作者不需要太擔心自動轉義。Python方面的開發人員(編寫檢視和自定義過濾器的人)需要考慮不應該轉義資料的情況,並適當地標記資料,因此只需在模板中工作即可。

如果您正在建立可能在您不確定是否啟用了自動轉義的情況下使用的模板,則將escape過濾器新增到需要轉義的任何變數中。當啟用autospace時,同時使用escape過濾器進行雙重轉義資料沒有危險,因為escape過濾器不會影響autospce的變數。

7.3 字串文字和自動轉義

正如我們前面提到的,過濾器引數可以是字串:
{{ data|default:"This is a string literal." }}

插入所有字串文字到模板中時不會做任何自動轉義 - 就好像它們都通過safe 過濾器一樣。這背後的原因是模板作者控制了字串文字中的內容,因此他們可以確保在編寫模板時正確轉義文字

這意味著你會寫

{{ data|default:"3 &lt; 2" }}
…而不是:
{{ data|default:"3 < 2" }} {# Bad! Don't do this. #}

這不會影響來自變數本身的資料會發生什麼。如有必要,變數的內容仍會自動轉義,因為它們超出了模板作者的控制範圍。

8. 訪問方法呼叫

附加到物件的大多數方法呼叫也可以從模板中獲得。這意味著模板可以訪問的不僅僅是類屬性(如欄位名稱)和從檢視傳入的變數。例如,Django ORM提供“entry_set”語法,用於查詢與外來鍵相關的物件集合。因此,給定一個名為“comment”的模型,該模型與稱為“task”的模型具有外來鍵關係,您可以迴圈遍歷給定任務的所有註釋,如下所示:

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

同樣,QuerySets提供了count()一種計算它們包含的物件數量的方法。因此,您可以使用以下方法獲取與當前任務相關的所有註釋的計數:
{{ task.comment_set.all.count }}

當然,您可以輕鬆訪問您在自己的模型上明確定義的方法:
例子見 訪問物件的方法

因為Django故意限制模板語言中可用的邏輯處理量,所以不可能將引數傳遞給從模板內訪問的方法呼叫。應在檢視中計算資料,然後傳遞給模板進行顯示。

9. 自定義標籤和過濾器庫

某些應用程式提供自定義標記和過濾器庫。要在模板中訪問它們,請確保應用程式在INSTALLED_APPS(我們為此示例新增'django.contrib.humanize' ),然後在模板中使用load標記載入:

{% load humanize %}

{{ 45000|intcomma }}

在上面,load標籤載入humanize標籤庫,然後使intcomma過濾器可供使用。如果已啟用django.contrib.admindocs,則可以查閱管理員中的文件區域以查詢安裝中的自定義庫列表。

load標籤可以載入多個庫名,用空格隔開。例:
{% load humanize i18n %}

有關編寫自己的自定義模板庫的資訊,請參見自定義模板(的標籤(標籤)和過濾器(filters)

9.1 自定義庫和模板繼承

載入自定義標籤或過濾器庫時,標籤/過濾器僅可用於當前模板 - 而不是模板繼承路徑中的任何父模板或子模板。

例如,如果模板foo.html具有{% load humanize %},則子模板(例如,具有{% extends "foo.html" %}的模板)將無法訪問humanize模板標籤和過濾器。子模板負責自己的{% load humanize %}

這是為了可維護性和理智性的特徵。

參見
模板參考
涵蓋內建標籤,內建過濾器,使用替代模板,語言等。