第3章Jinja 2模闆引擎
在Flask中通常使用Jinja 2模闆引擎來實作複雜的頁面渲染。Jinja 2被認為是靈活、快速安全的模闆引擎技術,被廣泛使用。Jinja 2的設計思想來源于Django模闆引擎, 它功能強大、速度 快,并且提供了可選的沙箱模闆執行環境安全保證機制,具有沙箱中執行、強大的HTML轉義系統、模闆繼承等諸多優點。本章主要介紹Jinja 2模闆引擎的基本結構、基本使用方法。
本章主要涉及的知識點有:
- 如何使用Flask渲染模闆;
- 在模闆中傳遞一個或多個參數;
- if語句在模闆中的使用;
- for語句在模闆中的使用。
3.1模闆引擎概述及簡單使用
随着不同終端(個人PC、平闆電腦,手機、移動穿戴裝置等)的興起,開發人員在越來越多地思考:如何寫一份功能代碼(業務邏輯代碼),這份業務邏輯代碼能夠在響應式或非響應式裝置上都能使用。為了提升開發效率,開發人員開始高度重視前後端的分離,後端負責業務邏輯/資料通路,前端負責表現、互動邏輯,同一份業務邏輯代碼可應用于多個不同終端的視圖渲染。後端實際上實作的功能一般叫做業務邏輯,前端完成的功能一般叫做表現邏輯。如果把業務邏輯和表現邏輯混在一起,勢必造成系統耦合度高、代碼維護困難的現象,是以分離業務邏輯和表現邏輯,把變現邏輯交給視圖引擎,即網頁模闆,很有必要。
模闆實質上是一個靜态的包含HTML文法的全部或片段的文本檔案,也可包含由變量表示的動态部分。使用真實值替換網頁模闆中的變量,生成對應資料的HTML片段,這一過程稱為渲染。Flask提供了Jinja 2模闆引擎來渲染模闆,下面逐漸介紹其模闆渲染機制。
在PyCharm中建立一名稱為3-1的工程,在工程中templates的檔案夾下建立index.html檔案,代碼如下:

在工程中templates的檔案夾下建立user.html檔案,代碼如下:
app.py檔案的代碼如下:
01行表示導入Flask子產品;02行表示導入render_template子產品;03行表示Flask初始化;04行定義路由;05行定義視圖函數;06行使用render_template()方法渲染模闆;07行定義路由;08行定義視圖函數;09行使用render_template()方法渲染模闆;10行表示當子產品被直接運作時,代碼将被運作,當子產品是被導入時,代碼不被執行;11行表示開啟調試模式。
運作程式,得到如3.1圖所示結果。
Flask通過render_template()函數來實作模闆的渲染。要使用Jinja 2模闆引擎,需要使用from flask import render_template指令導入render_template函數。在視圖函數的return方法中,render_template()函數的首個參數聲明使用哪一個模闆檔案。
3.2向模闆中傳遞參數
Flask提供Jinja 2模闆引擎來渲染模闆的同時,還可以将程式中的參數或變量值傳遞給指定的模闆進行渲染。
在PyCharm中建立一名稱為3-2的工程,在工程中的templates檔案夾下建立index.html檔案,代碼如下:
在工程中templates檔案夾下建立user.html檔案,代碼如下:
01行表示導入Flask子產品;02行表示導入render_template子產品;03行表示Flask初始化;04行表示定義路由;05行表示定義視圖函數;06行渲染模闆;07行定義路由;08行表示定義視圖函數;09行表示渲染模闆并向模闆傳遞參數;10行表示當子產品被直接運作時,代碼将被運作,當子產品被導入時,代碼不被執行。
運作程式,得到如圖3.2所示結果。
render_template()函數第一個參數是指定模闆檔案的名稱,比如這裡的index.html和user.html。render_template( )函數的第二個參數為可選項,可以為空。比如index()視圖函數中的render_template('index.html'),這裡第二個參數為空。第二個參數不為空的話,一般用于向模闆中傳遞變量。這裡傳遞變量,一般是以鍵值對方式進行的。
01 @app.route('/')
02 def index():
03 title = 'python的鍵值對'
04 author='tom_jack'
05 return render_template('index.html', var1=title, var2=author)
用上述代碼替換index()視圖函數代碼,在index.html的body塊兒區域增加下面的代碼:
01 <body>
02 {{ var1 }}<br> {#br表示網頁中的回車#}
03 {{ var2 }}
04 </body>
再次運作程式,得到如圖3.3所示結果。
模闆中接收變量值,需要把變量放在{{ }},比如{{ var1 }}等。模闆中如果要寫注釋,格式為{# #},比如這裡的{#br表示網頁中的回車#}。
如果視圖函數中有多個變量值都需要傳遞給模闆,可以使用**locals()方法,例如:
01 def index(): #定義index函數
02 # return render_template('index.html')
03 title = 'python的鍵值對' #定義鍵值
04 author = 'tom_jack' #定義鍵值
05 return render_template('index.html', **locals()) #渲染模闆并傳值
實際上是将return render_template('index.html', var1=title, var2=author)這行代碼替換為return render_template('index.html', **locals())。将模闆檔案index.html中的{{ var1 }}
{{ var2 }}替換為{{ title }}
{{ author }}即可。
注意:在render_template()函數中,如果要給模闆傳遞全部的本地變量,可以使用**locals()方法,此時,在子產品中可以直接使用{{title}}和{{author}}來直接使用變量。
3.3模闆中的控制語句之if語句
在Jinja 2模闆引擎中也可以使用if和for循環控制語句,控制模闆渲染的方向。模闆引擎中,if和for語句中應該放到{% %}中。
本節我們首先看看模闆中的if語句如何使用。在前端的Jinja 2文法中,if可以進行判斷:是否存在參數,存在的參數是否滿足條件,其基本文法如下:
01 {% if condition %} <!-- condition指的是條件-->
02 {% else %} <!-- 條件不滿足時-->
03 {% endif %} <!-- 結束if語句-->
在PyCharm中建立一個名稱為3-3的工程。在工程中的templates檔案夾下建立index.html檔案,代碼如下:
app.py對應的代碼如下:
import random表示導入Python的随機庫,rand1=random.randint(0,1)表示産生0~1範圍内的整型數。在模闆中進行判斷,如果産生的資料為1,視為有效,如果産生的資料為0,視為無效資料。運作本項目代碼,結果如圖3.4所示。可以多次重新整理,看看輸出結果有何不同。
在PyCharm中建立一名稱為3-4的工程。在工程中的templates檔案夾下建立index.html檔案,代碼如下:
app.py檔案内容如下:
運作項目代碼,運作結果如圖3.5所示。
在模闆中,盡量少使用多層嵌套的 if…else…語句,往往會因為縮進出現這樣或那樣的問題。盡量多用if…elif…else…的結構(即多個elif),這一系列條件判斷會從上到下依次判斷,如果某個判斷為True,執行完對應的代碼塊,後面的條件判斷就會直接忽略,不再執行。
3.4模闆中的控制語句之for語句
首先,我們回顧一下Python中的for循環語句。for循環語句是Python中的一個循環控制語句,任何有序的序列對象内的元素都可以周遊,比如字元串、清單、元組等可疊代對像。for循環的文法格式如下:
for 目标 in 對象:
循環體
比如,使用for循環一個字元串,輸出字元串中每位字元的操作方法如下:
01 #encoding:utf-8 #指定編碼
02 str='www.google.com' #定義字元串
03 for str1 in str: #for循環進行周遊
04 print(str1) #列印輸出
運作程式,螢幕上可以輸出www.google.com中每一個字元。那麼模闆中的for循環又該如何使用呢?模闆中的for語句定義如下:
01 {% for 目标 in對象 %}
02 <p>目标</p>
03 {% endfor %}
Jinja 2中for循環内置常量:
- loop.index:目前疊代的索引(從1開始);
- loop.index0:目前疊代的索引(從0開始);
- loop.first:是否是第一次疊代,傳回True或False;
- loop.last是否是最後一次疊代,傳回True或False;
- loop.length:傳回序列的長度。
下面以視圖函數定義一個字典goods,在模闆中使用for循環渲染輸出。在PyCharm中建立一名稱為3-5的工程。在工程中的templates檔案夾下建立shop.html檔案,代碼如下:
上面的代碼實作了一個靜态頁面,代碼中定義了一個表格,表格分為5行2列進行顯示,其中,第1行用來顯示表格“商品名稱”及“商品價格”等内容。
app.py檔案的内容如下:
在hello_world視圖函數中定義一清單goods,其屬性主要有name和price,用for語句将其周遊出來。運作程式,運作結果如圖3.6所示。
3.5Flask的過濾器
過濾器本質上是一個轉換函數,有時候我們不僅需要輸出變量的值,還需要把某個變量的值修改後再顯示出來,而在模闆中不能直接調用Python中的某些方法,這麼這就用到了過濾器。
3.5.1常見過濾器
1.與字元串操作相關的過濾器
<p>{{name|default('None',true)}</p>
其中,name為變量名,如果name為空,則用None這個值去替換name。
-
<p>{{'hello'|capitalize}}</p>
将字元串hello轉化成Hello,實作首字母大寫的目的。
<p>{{'HELLO'|lowere}}</p>
将字元HELLO全部轉為小寫。
<p>{{'hello'|replace('h','x')}}</p>
将hello中的字母h替換成x。
2.對清單進行操作相關的過濾器
-
{{[01,80,42,44,77]|first}}
取得清單中的首個元素01。
-
{{[01,80,42,44,77]|last}}
取得清單中的最後一個元素77。
-
{{[01,80,42,44,77]|count}}
取得清單中的元素個素,統計個數為5,count也可以使用length替換。
-
{{[01,80,42,44,77]|sort}}
清單中的元素重新排序,預設按照升序進行排序。
-
{{[01,80,42,44,77]|join(','}}
将清單中的元素合并為字元串,傳回1,80,42,44,77。
3.對數值進行操作相關的過濾器
-
{{18.8888|round}}
四舍五入取得整數,傳回19.0。
-
{{18.8888|round(2,’floor’)}}
保留小數點後2位,傳回結果為18.88。
- {{-2|abs}}
求絕對值運算,傳回結果為2。
下面以清單中的每間隔2行換顔色為例,詳細說明模闆中過濾器的使用方法。
在PyCharm中建立一名稱為3-6的工程。在工程中的templates檔案夾下建立index.html和app.py檔案,index.html檔案代碼如下:
3.5.2自定義過濾器
内置的過濾器不滿足需求怎麼辦?過濾器的實質就是一個轉換函數,我們其實完全可以寫出屬于自己的自定義過濾器。
通過調用應用程式執行個體的 add_template_filter 方法實作自定義過濾器。該方法第一個參數是函數名,第二個參數是自定義的過濾器名稱。
有一個商品清單頁,要求每3行輸出一條分割線。在PyCharm中建立一名稱為3-7的工程。在工程中的templates檔案夾下建立index.html和app.py檔案,index.html檔案代碼如下:
上面的代碼對傳遞過來的清單進行周遊,每3行輸出一條分割線,分割線的樣式由07~13行所對應的代碼定義。
02~05行導入相應子產品,有非UTF-8編碼範圍内的字元時就要使用sys.setdefaultencoding()方法予以修正。04行表示Flask初始化;05行表示定義路由;06行定義視圖函數;08~12行定義清單goods;13行表示渲染模闆,并向模闆傳遞參數;14行定義函數;15、16行表示每間隔3行傳回一個line;19行表示使用自定義過濾器添加CSS。
3.6 宏的定義及使用
Jinja 2中的宏功能有些類似于傳統程式語言中的函數,它跟Python中的函數類似,可以傳遞參數,但是不能有傳回值,可以将一些經常用到的代碼片段放到宏中,然後把一些不固定的值抽取出來作為一個變量。
3.6.1 宏的定義
宏(Macro),有聲明和調用兩個部分。讓我們先聲明一個宏:
01 <!--定義宏-->
02 {% macro input(name, type='text', value= ' ') -%}
03 ????<input type="{{ type }}" name="{{ name }}" value="{{ value|e }}">
04 {%- endmacro %}
上面的代碼定義了一個宏,定義宏要加macro,宏定義結束要加endmacro标志。宏的名稱就是input,它有3個參數,分别是name、type和value,後兩個參數有預設值。我們可以使用表達式來調用這個宏:
01 <!--調用宏-->
02 {{ input('username')}}
03 {{ input('password',type='password')}}
在Pycharm中建立一名為3-8的工程。在工程中templates的檔案夾下建立index.html檔案,index.html代碼如下:
執行網頁後,生成對應的代碼如下:
上面的代碼定義了一個宏,這個宏有3個參數,分别是name、type和value,然後用這個宏定義了兩個文本輸入框,定義了一個送出按鈕。
app.py檔案中需要引入模闆檔案,如下:
01行導入Flask和render_template子產品;02行表示Flask初始化;03行表示定義路由;04行表示定義視圖函數;05行渲染模闆;06行表示當子產品被直接運作時,代碼将被運作,當子產品是被導入時,代碼不被執行;07行表示開啟調試模式。
運作上面的程式,結果如圖3.7所示。
3.6.2 宏的導入
一個宏可以被不同的模闆使用,是以我們建議将其聲明在一個單獨的模闆檔案中。需要使用時導入即可,而導入的方法類似于Python中的import。我們把3.6.1節中的宏定義部分單獨放在一個檔案中。
在Pycharm中建立一名稱為3-9的工程。在工程中的templates檔案夾下建立index.html檔案和form.html檔案。index.html檔案代碼如下:
form.html檔案代碼如下:
上面的代碼定義了一個宏,定義宏要加macro,宏定義結束要加endmacro标志。宏的名稱就是input,它有3個參數,分别是name、type和value,後兩個參數有預設值。
01行表示導入Flask及render_template子產品;02行表示Flask初始化;03行表示定義路由;04行定義視圖函數;05行渲染模闆。
3.6.3 include的使用
宏檔案中引用其他宏,可以使用include語句。include語句可以把一個模闆引入到另外一個模闆中,類似于把一個模闆的代碼複制到另外一個模闆的指定位置。下面通過一個執行個體來說明。
在PyCharm中建立一個名為3-10的工程。在工程中的templates檔案夾下建立index.html檔案、header.html檔案及footer.html檔案。index.html檔案代碼如下:
header.html檔案内容如下:
footer檔案内容如下:
01行表示導入Flask及render_template子產品;02行表示Flask初始化;03行定義路由;04行定義視圖函數;05行表示渲染模闆。
include把一個模闆的代碼複制到另外一個模闆的指定位置,這裡的{% include ''header.html'' %}和{% include ''footer.html'' %}把頭檔案和尾部檔案引入到index.html檔案中。
3.7 set和with語句的使用
set與with語句都可以在Jinja 2中定義變量并賦予值。set定義的變量在整個模闆範圍内都有效,with關鍵字在定義變量并指派的同時,限制了with定義變量的作用範圍。
首先介紹一下set關鍵字的使用方法:
(1)給變量指派:
{% set telephone ='1388888888' %}
(2)給清單或數組指派:
{% set nav = [('index.html', 'index'), ('product.html', 'product)] %}
可以在模闆中使用{{ telephone }}和{{ nav }}來引用這些定義的變量。
接下來介紹with關鍵字的使用方法,例如:
{% with pass = 60 %}
{{ pass }}
{% endwith %}
with定義的變量的作用範圍在{% with %}和{% endwith %}代碼塊内,在模闆的其他地方,引用此變量值無效。
在PyCharm中建立一名為3-11的工程。在工程中templates的檔案夾下建立index.html檔案,index.html檔案代碼如下:
運作上面的工程,結果如圖3.8所示。
3.8 靜态檔案的加載
靜态檔案的加載一般需要先建立檔案夾static,在檔案夾下再建立css、js和images檔案夾,在這些檔案夾中存放css、js、images,同時要需要使用'url_for'函數。
在PyCharm中建立一個名為3-12的工程。找到static檔案夾,在此檔案夾下再建立css、js和images這3個檔案夾,目錄結構如圖3.9所示。
在templates目錄下建立一個名為index.html的檔案, 在app.py的視圖函數中使用return render_template('index.html')方法來渲染模闆。下面分别給出加載JS、圖檔和CSS的方法。
(1)加載JS檔案
在靜态檔案index.html中,在之前引入jquery-3.3.1.js檔案,具體代碼如下:
<script src="{{ url_for('static', filename='js/jquery-3.3.1/jquery-3.3.1.js') }}">
</script>
可以使用下面代碼測試jquery-3.3.1.js檔案是否加載成功。
<script>
if(jQuery) {
alert('jQuery已加載!');
}
else {
alert('jQuery未加載!');
}
</script>
通過測試,可以發現通過上述方法可以正常加載js檔案。這裡使用到了url_for()函數來實作。事實上,還可以通過下面代碼實作:
<script type="text/javascript" src="static/js/jquery-3.3.1/jquery-3.3.1.js">
</script>
不過,一般建議使用url_for()函數形式。
(2)加載圖檔檔案。
加載圖檔,可以使用下述代碼實作:
<img src="{{ url_for('static', filename='images/car.jpg') }}"></img>
(3)加載CSS檔案。
加載外部CSS檔案,可以使用下述代碼實作:
<link rel="stylesheet" href="{{ url_for('static',filename='css/car.css') }}">
car.css的代碼如下:
.img{
BORDER-RIGHT: #000 1px solid; BORDER-TOP: #000 1px solid; MARGIN: 10px 0px; BORDER-LEFT: #000 1px solid; BORDER-BOTTOM: #000 1px solid
}
在index.html檔案中添加如下代碼:
運作上述代碼,效果如圖3.10所示。
3.9 模闆的繼承
一個系統網站往往需要統一的結構,這樣看起來比較“整潔”。比如,一個頁面中都有标題、内容顯示、底部等幾個部分。如果在每一個網頁中都進行這幾部分的編寫,那麼整個網站将會有很多備援部分,而且編寫的網頁程式也不美觀。這時可以采用模闆繼承,即将相同的部分提取出來,形成一個base.html,具有這些相同部分的網頁通過繼承base.html來得到對應的子產品。
1.模闆的繼承文法
模闆的繼承文法如下:
{% extends “模闆名稱” %}
2.塊的概念
模闆繼承包含基本模闆和子模闆。其中,基本模闆裡包含了網站裡基本元素的基本骨架,但是裡面有一些空的或不完善的塊(block)需要用子模闆來填充。
在父模闆中:
…
{% block block的名稱 %}
{% endblock %}
…
在子模闆中:
…
{% block block的名稱 %}
子模闆中代碼
{% endblock %}
…
在PyCharm中建立一個名為3-13的工程。在templates目錄中建立index.html、base.html和product.html 3個靜态檔案。base.html檔案作為基類,index.html和product.html檔案作為子類,子類去繼承基類的基本内容。
base.html檔案内容如下:
index.html檔案内容如下:
product.html檔案的内容:
預設情況下,子模闆如果實作了父模闆定義的block,那麼子模闆block中的代碼就會覆寫父模闆中的代碼。如果想要在子模闆中仍然保持父摸闆中的代碼,那麼可以使用{{super()}}來實作,如index.html中{% block body %}{% endblock %}代碼塊中使用了{{ super() }}方法,運作結果如圖3.11所示:
如果想要在一個block中調用其他block中的代碼,可以通過{{self.其他block名稱()}}實作。比如product.html檔案中的
{{ self.title() }}
方法。運作此代碼,結果如圖3.12所示。
3.10 溫 故 知 新
1.學完本章内容後,讀者需要回答:
(1)什麼是模闆?
(2)模闆中如何寫注釋?
(3)模闆中如何使用if語句?
(4)模闆中如何使用for語句?
2.在下一章中将會學習:
(1)路由函數的使用。
(2)裝飾器的基本使用。
(3)藍圖的定義和基本使用。
3.11 習 題
通過下面的習題來檢驗本章的學習情況,習題答案請參考本書配套資源。
1.使用for語句,建立一個工程,列印出九九乘法表。
2.在視圖函數中定義一個字典books,請在模闆中周遊出字典的所有屬性。