在以前的文章中,簡單介紹了Python Web開發框架Flask,知道了如何寫個Hello World,可是距離用Flask開發真正的項目,還有段距離,如今咱們目標更靠近一些 —— 學習下Jinja2模板。html
模板是用來作什麼的呢?模板是用來更高效地生成相應時的Html文本的,沒有模板,能夠手寫,好比以前的hello world示例,寫段html代碼:python
<h1>Hello world!</h1>
對於簡單的練習還行,但對於規模大的,動態化程度高的項目來講,這樣寫就有些勉強了,即,不利於項目和產品化。那麼模板有什麼好處呢:linux
能讓展示邏輯和業務邏輯 展現邏輯即UI,就是用來給用戶看和操做的,業務邏輯是業務規則,好比什麼條件能夠註冊,什麼權限能考到什麼。模板將展示邏輯封裝起來,業務邏輯寫在視圖函數中。git
能使項目更易維護 因爲展示邏輯和業務邏輯的分離,它們能夠由不一樣的開發人員來維護,不會有代碼衝突的問題程序員
使項目更加安全 在作交互式開發中,有個原則: 永遠不要相信用戶的輸入,由於惡意用戶可能經過輸入來注入(關於注入之後有機會能夠單獨聊聊),而模板在必定程度上會防注入,例如用戶輸入一點html代碼做爲輸入,默認狀況下模板會將其替換爲網絡安全字符,以防止惡意注入。github
能提升開發效率 有了模板,至關於一個展現邏輯的函數,因此就能夠被複用,能夠用在不一樣的視圖函數中,也能夠用在不一樣的項目中面試
思考下:上面提到的展示邏輯和業務邏輯,爲何不直接說成前臺和後臺呢? 若是你有答案和想法,歡迎留言討論。編程
Jinja2是Flask框架默認支持的模板引擎,並非惟一也不是最好(因人而異,沒有最好)模板引擎,不一樣的Web框架,好比Django、Nodejs等都有本身的模板引擎,甚至一些程序員本身實現的模板引擎(我就這麼幹過),但大致思路是同樣的,都是要將數據替換或者轉換到,用特殊格式標記了位置的模板中,以合成動態的html,這種技術不新鮮,在以前的打印模板,如水晶報表裏就有,無非就是標記和語法不一樣而已,因此要觸類旁通。flask
像其餘功能同樣,要使用模板引擎,先引入api
from flask import render_template
注意:要將將模板文件放置在項目根目錄(即
print(__file__)
顯示的路徑)下的templates
文件夾中
例如模板文件 hello.html
爲:
<h1>Hello {{ name }} </h1>
{% endraw %}
視圖函數能夠寫成:
@app.route('/user/<name>')def index(name): return render_template('hello.html', name=name)
Flask提供的 render_template
函數把Jinja2模板引擎集成到了程序中。render_template
函數第一個參數是模板的文件名,隨後的參數都是鍵值對,表示模板中變量的對應的真實值,在上面代碼中,模板會接收到一個名爲 name
的變量
模板文件就是普通的文本文件,而後將須要替換的部分用雙大括號( {{}}
)標記出來,雙大括號中,表示要替換的變量名,這個變量支持基本數據類型,以及列表、詞典、對象和元組。如模板 template.html
:
<p> A value form a string: {{ name }}.</p><p> A value form a int: {{ myindex }}.</p><p> A value form a list: {{ mylist[3]] }}.</p><p> A value form a list, with a variable index: {{ mylist[myindex] }}.</p><p> A value form a dictionary: {{ mydict['key'] }}.</p><p> A value form a tuple: {{ mytuple }}.</p><p> A value form a tuple by index: {{ mytuple[myindex] }}.</p>
{% endraw %}
視圖函數代碼:
@app.route('/template/')def template(): name = 'Jinja2 模板引擎' myindex = 1 mylist = [1,2,3,4] mydict = { key: 'age', value: '25' } mytuple = (1,2,3,4) return render_template('template.html', name=name, myindex=myindex, mylist=mylist, mydict=mydict, mytuple=mytuple)
顯示結果:
有些時候須要對要在模板中替換的值作一些特殊處理,好比首字母大寫,去掉先後空格等等,有種選擇就是使用過濾器。
Jinjia2模板引擎中,過濾器相似於Linux命令中的管道,例如將字符串變量的首字母大寫
{% raw %}
<h1>{{ name | capitalize}}</h1>
{% endraw %}
過濾器能夠拼接,和linux的管道命令同樣,如對值進行所有變大寫,而且去除先後空白字符:
{% raw %}
<h1>{{ name | upper | trim }}</h1>
{% endraw %}
如上代碼,過濾器和變量之間用管道符號 | 相連,至關於對變量值做進一步加工。
一些經常使用的過濾器
過濾器 | 說明 |
---|---|
safe | 渲染是不轉義 |
capitalize | 首字母大寫 |
lower | 全部字母小寫 |
upper | 全部字母大寫 |
title | 值中每一個單詞首字母大寫 |
trim | 刪除首位空白字符 |
striptags | 渲染時刪除掉值中全部HTML標籤 |
注意:safe過濾器,默認狀況下,處於安全考慮,Jinja2會轉義全部變量,例如一個變量的值爲 <h1>Hello</h1>
, Jinja2會將其渲染成 <h1>Hello</>
,瀏覽器會顯示出本來的值,可是不會解釋。若是須要瀏覽器解釋的話,可使用 safe 過濾器 例如模板文件 html.html
爲:
{% raw %}
<h1>{{ html | safe }}</h1>
{% endraw %}
視圖函數爲:
@app.route('/html')def html(): return render_template('html.html', html='<b>bob</b>')
注意:千萬別在不可信的值上使用 safe 過濾器,例如用戶在表單上輸入的文本。
還有一些有用的過濾器
default
,能夠當變量未定義時,提供默認值,若是想將 false
、 False
和空( none
)視爲未定義,須要提供第二個參數爲 true
{% raw %}
{% endraw %}
當變量 name
的未定義時,上下兩個顯示效果同樣,當值爲 none
時,上面會顯示 Hellonone!
, 而下面的會顯示 Helloworld!
<!-- 提供默認值過濾器 -->
<h1>Hello {{ name | default('world') }}!</h1>
<!-- 將false、False和空(none)視爲未定義的默認值過濾器 -->
<h1>Hello {{ name | default('world', true)! }}</h1>
列表過濾器 min
, max
, 獲得列表中的最小值或最大值
過濾器雖然有不少,但總有不知足需求的時候,例如首行文字縮進、將金額轉化爲中文的大寫等等。過濾器實質就是個函數,因此,第必定義一個過濾器函數,第二,註冊到Jinjia2的過濾器中。
# 定義過濾器函數
def mylen(arg):# 實現一個能夠求長度的函數
return len(arg)
def interval(test_str, start, end): # 返回字符串中指定區間的內容
return test_str[int(start):int(end)]
# 註冊過濾器
env = app.jinja_env
env.filters['mylen'] = mylen
env.filters['interval'] = interval
# 視圖函數
@app.route('/myfilter')
def myfilter():
return render_template('myfilter.html', phone='13300000000')
模板文件
{% raw %}
<h1>電話號碼是:{{ phone }}, 長度爲:{{ phone | mylen }},運營商號:{{ phone | interval(0,3) }}</h1>
{% endraw %}
過濾器註冊代碼還能夠寫在初始化代碼
__init__.py
中
不少時候,須要更智能的模板渲染,即能給渲染編程,好比男生一個樣式,女生同樣樣式,控制結構指令須要用指令標記來指定,下面介紹下一些簡單的控制結構
即在模板中用 if-else
控制結構
{% raw %}
{% if gender=='male' %} Hello, Mr {{ name }}{% else %} Hello, Ms {{ name }}{% endif %}
{% endraw %}
視圖函數
@app.route('/hello2/<name>/<gender>')def hello2(name, gender): return render_template('hello2.html', name=name, gender=gender)
在控制結構裏,代碼語法同 python
循環對於渲染列表,頗有幫助,循環的標記是 for
。例如獎列表的內容顯示在 ul
中
{% raw %}
<ul>{% for name in names %} <li>{{ name }} </li>{% endfor %}</ul>
{% endraw %}
例如給定一個學生列表,將其用無序列表 ul
顯示出來
模板中能夠定義宏,至關於定義了一個函數,能夠重複使用,讓邏輯更清晰。首先,定義一個宏:
mymacro.html {% raw %}
{% macro render_name(name) %} <li>{{ name }}</li>{% endmacro %}
{% endraw %} 而後使用宏, 例如將循環結構的例子中,顯示名稱的地方,改成調用宏 {% raw %}
<ul> {% for name in names %} {{ render_name(name) }} {% endfor %}</ul>
{% endraw %}
調用宏,和調用函數是同樣的,不過要將代碼寫在 {{}}
雙大括號內。通常咱們會將宏存在單獨的文件中,以便複用,在須要用到宏的地方,引用就行了
{% raw %}
{% import 'mymarco.html' as macros %}<ul> {% for name in names %} {{ macros.render_name(name) }} {% endfor%}</ul>
{% endraw %}
如上所述,用improt引入宏定義文件,經過as指定別名,和python的模塊引入同樣。指定別名是一個良好的編程習慣,能夠將一個複雜的東西形象化,同時像一個命名空間同樣,有效的避免衝突。
另外能夠將多個模板片斷寫入一個單獨文件,再包含( include
)在全部模板中,以提升開發效率:
{% raw %}
{% include 'common.html' %}
{% endraw %}
include
進來的文件,至關於將文件中的內容複製到 include
的位置,因此自使用以前須要考慮仔細
若是以爲 include
過於呆板,靈活性差,Jinja2模板引擎還有更高級的功能——繼承。相似於Python代碼中類的繼承,一塊兒看看。首先定義一個基類, base.html: {% raw %}
<html><head> {% block head %} <title>{% block title %}{% endblock%} - My Application</title> {% endblock %}</head><body> {% block body %} <h3>這是基類的內容</h3> {% endblock %}</body></html>
{% endraw %}
其中的 block
標籤,定義了能夠被子類重構(替換)的部分,每一個 blcok
標籤,須要指定一個特殊的名稱,例如 head
、 title
等,以便子類用特定的名稱來重構。另外 block
標籤須要有結束標籤 endblock
,相似於類C語言中的大括號,固然 block
標籤也能夠嵌套。接下來,定義一個子類模板 hello3.html:
{% raw %}
{% extends "base.html" %}{% block title %}Index{% endblock %}{% block head %} {{ super() }} <style></style>{% endblock %}{% block body %} {{ super() }} <h3>這是子類的內容 Hello world!</h>{% endblock %}
{% endraw %}
效果如圖所示:
經過 extends
標記來指定須要繼承的基類,而後用 block
標記來設置子類須要替換調基類中的內容,只要 block
指定的名稱同樣就行。另外,如不須要徹底替換調基類的內容,能夠在子類 block
中,調用 super
方法,以獲取基類在此名稱下的內容,這樣就能達到更號的靈活性。
今天介紹了Jinja2模板引擎的基本用法和特色,指望經過不一樣的特色,讓你瞭解到模板的基本用法,以便更快的使用和進一步學習更深刻的內容。另外,想經過Jinja2模板引擎,說明模板的基本特徵,以便舉一反三、觸類旁通,更快的學習其餘優秀的模板, 同時也想說明,模板不只僅能夠用在Web的開發中,還能夠用在自動化編碼、測試等衆多領域。最後在本章開頭,留了個思考題,爲何不將展示邏輯和業務邏輯說成是前臺和後臺呢?若是你有答案,歡迎留言交流。
圖書: Flask Web開發 https://item.jd.com/12418677.html
API-Jinja Documentation(2,10.x) https://jinja.palletsprojects.com/en/2.10.x/api/#the-context
ansible基礎-Jinjia2模板——過濾器
https://www.cnblogs.com/mauricewei/p/10056379.html
參考代碼
https://github.com/JustDoPython/python-100-day/tree/master/day-020
系列文章