django-模板之標籤
模板
模版是純文字檔案,可以生成任何基於文字的檔案格式,比如HTML,XML,CSV等。Django模版語言致力於在效能和簡單性上取得平衡。Django的模版系統並不是簡單的將Python嵌入到HTML中。
下面是一個小模版,它展示了一些基本的元素。
{% 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 %}
注意:
呼叫物件裡面的方法的時候,不需要寫括號來執行,並且只能執行不需要傳引數的方法,如果你的這個方法需要傳引數,那麼模板語言不支援,不能幫你渲染
每一個Web框架都需要一種很便利的方法用於動態生成HTML頁面。 最常見的做法是使用模板。模板包含所需HTML頁面的靜態部分,以及一些特殊的模版語法,用於將動態內容插入靜態部分。
Django可以配置一個或多個模板引擎(語言),也可以不用引擎。Django自帶一個稱為DTL(Django Template Language )的模板語言,以及另外一種流行的Jinja2語言(需要安裝,pip install Jinja2)。
Django為載入和渲染模板定義了一套標準的API,與具體的後臺無關。載入指的是,根據給定的模版名稱找到的模板然後預處理,通常會將它編譯好放在記憶體中。渲染則表示,使用Context資料對模板插值並返回生成的字串。
語法
變數相關的用{{}},邏輯相關的用{%%}。
模板渲染的官方文件:(https://docs.djangoproject.com/en/1.11/ref/templates/builtins/#std:templatetag-for)
本文參考官方文件,即用的是DTL引擎。
Django模板語言的語法包括四種結構:變數、標籤、過濾器、註釋。
變數
變數的值來自context中的資料字典, 類似於字典物件的keys到values的對映關係。
在Django的模板語言中按此語法使用:{{ 變數名 }}。當模版引擎遇到一個變數,它將從上下文context中獲取這個變數的值,然後用值替換掉它本身。 變數的命名包括任何字母數字以及下劃線的組合。變數名稱中不能有空格或標點符號。
深度查詢據點符(.)在模板語言中有特殊的含義。當模版系統遇到點("."),它將以這樣的順序查詢:
- 字典查詢(Dictionary lookup),字典鍵查詢優先於方法查詢。
- 屬性或方法查詢(Attribute or method lookup)
- 數字索引查詢(Numeric index lookup)
注意事項:
- 如果計算結果的值是可呼叫的,它將被無引數的呼叫。 呼叫的結果將成為模版的值。
- 如果使用的變數不存在, 模版系統將插入 string_if_invalid 選項的值, 它被預設設定為'' (空字串) 。
- 像
{{ foo.bar }}
這種模版表示式中的“bar”,如果在模版上下文中存在,將解釋為一個字面意義的字串而不是使用變數bar的值 。
標籤
模版語言中的標籤類似Python中的函式,可以輸出內容、控制結構,甚至可以訪問其他的模板標籤。
{% csrf_token %} # csrf令牌標籤,用於POST提交。
部分標籤需要使用起始和閉合標籤。
for迴圈標籤
迴圈物件中每個元素。需要結束標籤{% endfor %}
。
顯示athlete_list中提供的運動員列表:
<ul>
{% for athlete in athlete_list %}
<li>{{ athlete.name }}</li>
{% endfor %}
</ul>
迴圈物件points的每個元素都是(x,y)這樣的二元元組,並返回:
{% for x, y in points %}
There is a point at {{ x }},{{ y }}
{% endfor %}
訪問一個字典中的鍵值:
{% for key, value in data.items %}
{{ key }}: {{ value }}
{% endfor %}
可以使用{% for obj in list reversed %}
進行反向迴圈。
下面是Django為for標籤內建的一些屬性,可以當作變數一樣使用{{ }}
在模版中使用。
forloop.counter:迴圈的當前索引值,從1開始計數;常用於生成一個表格或者列表的序號
forloop.counter0:迴圈的當前索引值,從0開始計數;
forloop.revcounter: 當前迴圈的倒序索引值(最後一個為1)
forloop.revcounter0 當前迴圈的倒序索引值(最後一個為0)
forloop.first:判斷當前是否迴圈的第一次,是的話,該變數的值為True。
forloop.last:如果這是最後一次迴圈,則為真
forloop.parentloop:對於巢狀迴圈,返回父迴圈所在的迴圈次數。
for … empty
for標籤帶有一個可選的{% empty %}
從句,以便在迴圈物件是空的或者沒有被找到時,可以有所操作和提示。
<ul>
{% for athlete in athlete_list %}
<li>{{ athlete.name }}</li>
{% empty %} # 若列表為空,則執行。
<li>Sorry, no athletes in this list.</li>
{% endfor %}
</ul>
if-elif-else標籤
需要{% endif %}
結束標籤。
{% 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 %}
還可以在if標籤支援 and 、or、==、>、<、!=、<=、>=、in、not in、is、is not
判斷等多種運算子,注意條件兩邊都有空格。也可使用過濾器。
{% if somevar == "x" %}
This appears if variable somevar equals the string "x"
{% endif %}
{% if athlete_list|length > 1 %}
Team: {% for athlete in athlete_list %} ... {% endfor %}
{% else %}
Athlete: {{ athlete_list.0.name }}
{% endif %}
# 注意,大多數模版過濾器都返回字串型別,所以使用過濾器做整數型別的比較通常是錯誤的,但length是一個例外。
操作符都可以組合以形成複雜表示式。操作符的優先順序從低至高如下:
or
and
not
in
==,!=,<,>,<= ,>=
與Python的規則是一樣的。
如果想要不同的優先順序,那麼需要使用巢狀的if標籤,而不能使用圓括號。比較運算子不能像Python或數學符號中那樣“連結”。
{% if a > b > c %} (錯誤的用法)
{% if a > b and b > c %} (正確的用法)
{% if athlete_list and coach_list %}
Both athletes and coaches are available.
{% endif %}
{% if not athlete_list %}
There are no athletes.
{% endif %}
{% if athlete_list or coach_list %}
There are some athletes or some coaches.
{% endif %}
{% if not athlete_list or coach_list %}
There are no athletes or there are some coaches.
{% endif %}
{% if athlete_list and not coach_list %}
There are some athletes and absolutely no coaches.
{% endif %}
with
使用一個簡單地名字快取一個複雜的變數,多用於給一個複雜的變數起別名。當需要使用一個代價較大的方法(比如訪問資料庫)很多次的時候這是非常有用的。
像這樣:
{% with total=business.employees.count %}
{{ total }} <!--只能在with語句體內用-->
{% endwith %}
或:
{% with business.employees.count as total %}
{{ total }}
{% endwith %}
可以分配多個變數:
{% with alpha=1 beta=2 %}
...
{% endwith %}
注意等號左右不要加空格。
csrf_token
若不使用此標籤以post方式提交表單時,會報錯。如果在settings裡面的中介軟體配置裡把csrf的防禦機制給登出,則不會報錯,但不安全。這個標籤用於跨站請求偽造保護。
在頁面的form表單裡面任何位置寫上{% csrf_token %},在模板渲染的時將被替換成類似於下方的input標籤:
<input type="hidden" name="csrfmiddlewaretoken" value="8J4z1wiUEXt0gJSN59dLMnktrXFW0hv7m4d40Mtl37D7vJZfrxLir9L3jSTDjtG8">
這個標籤的值是個隨機字串,提交的時候,值也被提交了,首先這個標籤是後端渲染給頁面加上的,那麼當通過form表單提交資料時候,後臺django有相同的一個值,可以做對應驗證是不是我給你的token,如果使用者沒有按照這個正常的頁面來post提交表單資料,那麼就能知道這個請求是非法的,反爬蟲或者惡意攻擊網站
comment
註釋
註釋可以包含任何模版內的程式碼,有效的或者無效的都可以。當要註釋掉一些程式碼時,可以用此來記錄程式碼被註釋掉的原因。單行註釋語法:{# #}
{# this won't be rendered #} # 單行註釋
{% comment %}
標籤提供多行註釋功能。
在{% comment %}
和{% endcomment %}
之間的內容會被忽略,作為註釋。例如:
<p>Rendered text with {{ pub_date|date:"c" }}</p>
{% comment "Optional note" %}
<p>Commented out text with {{ create_date|date:"c" }}</p>
{% endcomment %}
comment標籤不能巢狀使用。
block和extends標籤
繼承和複寫模版。類似Python的類繼承和重寫機制。
extends標籤表示當前模板繼承自一個父模板。
這個標籤可以有兩種用法:
- {% extends "base.html" %}:繼承名為"base.html"的父模板
- {% extends variable %}:使用variable變量表示的模版
模板繼承
Django模版引擎中最強大也是最複雜的部分就是模版繼承了。模版繼承允許你建立一個包含基本“骨架”的父親模版,它包含站點中的共有元素,並且可以定義能夠被子模版覆蓋的blocks。通過下面這個例子,理解模版繼承的概念:
<!DOCTYPE html>
<html lang="en">
<head>
<link rel="stylesheet" href="style.css" />
<title>{% block title %}My amazing site{% endblock %}
</title>#注意此處,block標籤。
</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骨架。
“子模版”需要做的是先繼承父模板base.html
,然後複寫、填充,或者說實現其中的blocks。
block是在子模版中可能會被覆蓋掉的位置。在上面的例子中,block標籤定義了三個可以被子模版內容填充的block,分別是title、content和siderbar。
子模版可能看起來是這樣的:
{% extends "base.html" %} #extends標籤是這裡的關鍵。它告訴模版引擎,這個模版“繼承”了另一個模版。當模版系統處理這個模版時,首先會去載入父模版,也就是“base.html”。
{% block title %}My blog{% endblock %} #修改base裡title標籤裡的內容。
{% block content %}
{% for entry in blog_entries %}
<h2>{{ entry.title }}</h2>
<p>{{ entry.body }}</p>
{% endfor %}
{% endblock %}
# 載入過程中,模版引擎將注意到base.html中的三個block標籤,並用子模版中的內容來替換這些block。
請注意,上面例子中的子模版並沒有定義sidebar block
,這種情況下,將使用父模版中的內容。父模版的{% block %}
標籤中的內容總是被用作預設內容。
Django多級繼承常用方式類似下面的三級結構:
- 建立一個
base.html
模版,用來控制整個站點的主要視覺和體驗。 - 為站點的每一個app,建立一個
base_SECTIONNAME.html
模版。 例如base_news.html
,base_sports.html
。這些模版都繼承base.html
,並且包含了各自特有的樣式和設計。 - 為每一個頁面型別,建立獨立的模版,例如新聞內容或者部落格文章。 這些模版繼承對應app的模版。
上面的方式可以使程式碼得到最大程度的複用,並且使得新增內容到共享的內容區域更加簡單,例如app範圍內的導航條。
使用繼承的一些相關說明:
如果在模版中使用
{% extends %}
標籤,它必須是模版中的第一個標籤,必須放在檔案首行。在base模版中設定越多的
{% block %}
標籤越好。子模版不必定義全部父模版中的blocks,所以可以在大多數blocks中填充合理的預設內容,然後,只定義你需要的那一個。多一點鉤子總比少一點好。如果發現自己在複製大量重複的模版內容,那意味著應該把重複的內容移動到父模版中的一個
{% block %}
中。如果需要獲取父模板中的block的內容,想要在父block中新增內容而不是完全覆蓋它,可以使用
{{ block.super }}
變數。使用{{ block.super }}
插入的資料不會被自動轉義,因為父模板中的內容已經被轉義。在
{% block %}
之外建立的變數使用模板標籤的as
語法,不能在塊內使用。例如,下面的模板不會顯示任何內容:
{% trans "Title" as title %}
{% block content %}{{ title }}{% endblock %}
為了更好的可讀性,可以給
{% endblock %}
標籤一個取名字,如:{% block content %}
。在大型模版中,這有助於你清楚的看到哪一個{% block %}
標籤被關閉了。不能在一個模版中定義多個相同名字的block標籤。
Django1.10中添加了使用相對路徑的能力。通常模板名稱是相對於模板載入器的根目錄。字串引數也可以是以./
或../
開頭的相對路徑。 例如,假設有以下目錄結構:
dir1/
template.html
base2.html
my/
base3.html
base1.html
在template.html中,以下路徑將有效:
{% extends "./base2.html" %}
{% extends "../base1.html" %}
{% extends "./my/base3.html" %}
url
於url的反向解析。返回與給定檢視和可選引數匹配的絕對路徑引用(不帶域名的URL)。在解析後返回的結果路徑字串中,每個特殊字元將使用iri_to_uri()
編碼。這可以避免在模板中硬編碼超級連結路徑。
{% url 'some-url-name' v1 v2 %}
第一個引數是url()
的名字。 它可以是一個被引號引起來的字串或者其他的上下文變數。其他引數是可選的並且以空格隔開,這些值會在URL中以引數的形式傳遞。上面的例子展示瞭如何傳遞位置引數,也可以使用關鍵字引數。
{% url 'some-url-name' arg1=v1 arg2=v2 %}
不要把位置引數和關鍵字引數混在一起使用。URLconf所需的所有引數都應該提供。
例如,假設有一個檢視app_views.client
,其URLconf接受客戶端ID,並如下所示:
('^client/([0-9]+)/$', app_views.client, name='app-views-client')
如果你的應用中的URLconf已經被包含到專案URLconf中,比如下面這樣
('^clients/', include('project_name.app_name.urls'))
然後,在模板中,你可以建立一個此檢視的連結,如下所示:
{% url 'app-views-client' client.id %}
模板標籤會輸出字串:/clients/client/123/
如果希望在不顯示網址的情況下檢索網址,可以使用略有不同的呼叫:
{% url 'some-url-name' arg arg2 as the_url %}
<a href="{{ the_url }}">I'm linking to {{ the_url }}</a>
如果檢視不存在,{% url ... as var %}
語法不會導致錯誤。
{% url 'some-url-name' as the_url %}
{% if the_url %}
<a href="{{ the_url }}">Link to optional stuff</a>
{% endif %}
如果使用urlconf的名稱空間網址,通過冒號指定完全名稱,如下所示:
{% url 'myapp:view-name' %}
include
元件
載入指定的模板並以標籤內的引數渲染。這是一種引入別的模板的方法,要將include和extend區分開,include類似Python的import。
{% include "foo/bar.html" %}
也可以使用變數名:
{% include template_name %}
下面這個示例生成輸出“Hello, John!”:
context:變數greeting="Hello",變數person="John"。
模板:
{% include "name_snippet.html" %}
name_snippet.html模板:
{{ greeting }}, {{ person|default:"friend" }}!
可以使用關鍵字引數將額外的上下文傳遞到模板:
{% include "name_snippet.html" with person="Jane" greeting="Hello" %}
如果僅使用提供的變數來渲染上下文,新增only選項。
{% include "name_snippet.html" with greeting="Hi" only %}
將子模版渲染並嵌入當前HTML中的變種方法,不是解析子模版並在被父模版包含的情況下展現其被父模版定義的內容。這意味著在不同的被包含的子模版之間並不共享父模版的狀態,每一個子模板包含都是完全獨立的渲染過程。
load
載入自定義模板標籤。
下面的模板將會從somelibrary和package包中的otherlibrary中載入所有已經註冊的標籤和過濾器。
{% load somelibrary package.otherlibrary %}
還可以使用from引數從庫中選擇性載入單個過濾器或標記。
{% load foo bar from somelibrary %}
Django內建標籤總覽
可以查詢下表來總覽Django的內建標籤: