其實我接觸過的web架構也就Java下的jsp和Python下的web.py和django、flask,但是他們都有類似模版語言的存在,有的是像Django一樣專門的創造,有的是簡單的在html文檔裡嵌入程式設計語句。但是毋庸置疑,這些“模版語言”是讓html檔案變得動态的根源。
模版語言定義(Django Template Language, DTL)
Django的模版語言十分簡明,很容易就能學會。我們用幾個判斷句來給模版語言定義。
-1 所有使用了模版語言的html檔案被我們稱為模版。
-2 在模版語言中,{% 語句 %}、{{ 輸出 }}、{# 注釋 #}。也就是說前者括号内的内容會被django執行,後者直接被運算後寫在檔案的相應位置上。
-3 在模版語言中,我們的變量可以使用|(過濾器)和.運算符,我們的語句可以用來載入内容或使用for/if/block/extends/url等标簽。不能直接運作python語言,這點要謹記。
{{ 輸出 }}
1 View與Template的互動
模版由視圖渲染并傳給用戶端,使用render函數。當模版中需要資料時,我們向render函數傳入同鍵名的字典。render函數的介紹在上一部分。
2 .運算符
DTL中的點運算符按照字典->對象->清單/元組查詢,當以上查詢都不成立的時候傳回空字元串。也就是說,如果你在前端傳入了一個數組a=[1,2,…],前端模版内你對其的通路應為a.1、a.2…
例1 view傳值:
示例項目根路徑下urls.py裡urlpatterns添加:
path("thirdurl",views.third)
子app helloworld下views.py:
def third(request):
if(request.method=="GET"):
dictionary={"key1":"1","key2":[1,2,3,4],"key3":{"a":"a","b":"b"}}
return render(request,"我的html.html",dictionary)
子app helloworld下檔案夾templates下我的html.html:
<!doctype html>
<html>
<head></head>
<body>
{{key1}}
{{key2.0}}
{{key3.a}}
</body>
</html>
項目下打開指令行runserver在127.0.0.1:8000下運作django,輸入127.0.0.1:8000/thirdurl,可以看到傳回了1 1 a。
3 |運算符與過濾器
過濾器可以被視為專門用來處理模版資料的python函數,在模版中,我們通過|運算符使用過濾器。Django内置了很多過濾器,這些可以在網上搜到,我們也可以自己制作過濾器。
-過濾器在使用之前需要使用{% load 過濾器檔案名 %}導入。
-過濾器檔案預設放在app目錄下templatetags檔案夾下。
-過濾器檔案中定義的函數即是過濾器,函數前要加上@register.filter(name="過濾器在模版中的名字"),整個檔案前要加上兩行:from django import template register=template.Library()。
例2 使用django自帶的過濾器:
示例項目根路徑下urls.py裡urlpatterns添加:
path("fourthurl",views.fourth)
子app helloworld下views.py:
def fourth(request):
if(request.method=="GET"):
dictionary={"key1":"1"}
return render(request,"我的html.html",dictionary)
子app helloworld下檔案夾templates下我的html.html:
<!doctype html>
<html>
<head></head>
<body>
{{key1.1|default:"不存在"}}
</body>
</html>
過濾器default為django自帶的過濾器,功能為在傳回空串時替代為後面的字元串。這個過濾器不需要導入(但是不是所有自帶的過濾器都不要)。項目下打開指令行runserver在127.0.0.1:8000下運作django,輸入127.0.0.1:8000/thirdurl,可以看到傳回了“不存在”,因為key1.1找不到值。
例3 一個小例子,自建過濾器:
子app helloworld下檔案夾templatetags檔案夾下建立block1.py:
from django import template
register=template.Library()
@register.filter(name="_reverse")#這個過濾器翻轉list
def _reverse(arg):
return reversed(arg)
在模版檔案中這麼引用:
<!doctype html>
<html>
<head></head>
<body>
{% load block1 %}
{{arg1|_reverse}}
</body>
</html>
{% 語句 %}
1 load語句
load語句用來載入自定義标簽或過濾器檔案,比如大名鼎鼎的{% load static %}。
# load static與 static ‘’
{% load static %}載入了static标簽,{% static ‘檔案路徑’ %}用于加載靜态檔案。
很多人把static标簽說的玄之又玄,其實不然。在沒有額外設定的情況下,django會去app目錄下的static目錄下尋找static引号裡字元串為路徑的檔案。(關于這些靜态檔案存放請參考我之前講的模版檔案的存放,請仔細思考django尋找檔案的順序和檔案的儲存順序,如果你直接随意存儲檔案,當其重名時django隻會傳回第一個查找的,這裡沒有對于app的就近原則)
-如果想要修改static目錄的名字,請修改settings.py檔案下的STATIC_URL。(根據url的解析規則你應該能意會這個變量定義了什麼)
-如果你的static不想放在app目錄下,可以在settings.py檔案下添加STATICFILES_DIRS變量,将其值設為你設定的static檔案夾,例如STATICFILES_DIRS= (os.path.join(BASE_DIR, "static"),),則靜态檔案夾為項目目錄下的static檔案夾。
2 for語句和if語句
DTL中的for語句和if語句的使用與python中相同,但是書寫方式有所不同。
{% for 表達式 %}内容{% endfor %} for語句
{% if 表達式 %}内容{% endif %} if語句
你可能注意到了兩個語句之間我寫的是内容而不是語句,事實上裡面也可以寫html标簽。不要想當然。
3 block語句
block語句擴住的内容可以被覆寫。我們通過引用模版并覆寫其中的block完成模版代碼的重用,如果不覆寫塊中的内容就是父模版中塊的内容。(可以想象一個父類和重寫父類方法的子類)這個特性可以很好的用于節省模版的編寫時間,也可以讓模版層次分明。
{% block 名稱 %}内容{% endblock 名稱 %} block語句
{% extends ‘模版檔案名稱’ %} 引用模版檔案
例4 覆寫block及引用static
項目app helloworld目錄下模版檔案夾下建立 父模版.html:
<!doctype html>
<html>
<head></head>
<body>
{% block myblock1 %}
<p>父内容</p>
{% endblock myblock1 %}
</body>
</html>
同目錄建立 子模版.html:
{% extends ‘父模版.html’ %}
{% block myblock1 %}
<p>子内容</p>
{% endblock myblock1 %}
很明顯,如果你的項目渲染了子模版,那麼會出現“子内容”。
{# 注釋 #}
關于這個注釋隻有一個需要注意的。Html檔案的注釋還會出現在檔案内,隻不過不被呈現出來,而DTL的注釋在渲染模版的時候就會被剔除。
關于DTL的一些思考
相比于直接在模版檔案中嵌入python語句,DTL顯得更加高明。因為它不但簡潔,而且從架構編寫者的方面就嚴格遵循了MTV模式,不需要使用架構者特意去注意這些。這就像“帶上腳鐐的舞蹈”,是有限制的自由,但是也更美。
Tips:本文裡涉及DTL的語句和标簽是等價的。