Flask 模板 – 變數、過濾器
- 2019 年 12 月 19 日
- 筆記
模板
Flask的視圖函數有兩個作用:處理業務邏輯和返迴響應內容。
在大型應用中,把業務邏輯和表現內容放在一起,會增加程式碼的複雜度和維護成本。這次的模板內容主要的作用即是承擔視圖函數的另一個作用,即返迴響應內容。
模板其實是一個包含響應文本的文件,其中用佔位符(變數)表示動態部分,告訴模板引擎其具體值需要從使用的數據中獲取。使用真實值替換變數,再返回最終得到的字元串,這個過程稱為「渲染」。
Flask使用Jinja2這個模板引擎來渲染模板。Jinja2能識別所有類型的變數,包括{}。 Jinja2模板引擎,Flask提供的render_template函數封裝了該模板引擎,render_template函數的第一個參數是模板的文件名,後面的參數都是鍵值對,表示模板中變數對應的真實值。
Jinja2官方文檔(http://docs.jinkan.org/docs/jinja2/)
模板的基本語法
先來認識下模板的基本語法:
<title>{% block title %}{% endblock %}</title> <ul> {% for user in users %} <li><a href="{{ user.url }}">{{ user.username }}</a></li> {% endfor %} </ul>
下面來寫一個簡單的示例:
- 導入Flask使用模板的方法
from flask import render_template
- 在templates創建一個index.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h3>name = {{ user_name }}</h3> <h3>age = {{ age }}</h3> </body> </html>
- 編寫使用模板的示例程式碼
from flask import Flask,render_template # 創建Flask的app應用 app = Flask(__name__) # index視圖函數 @app.route("/index") def index(): # 設置模板html,以及設置參數 return render_template('index.html', user_name='libai', age=18 ) if __name__ == '__main__': app.run(debug=True)
- 測試訪問視圖

可以看到,模板成功顯示出了參數內容。但是通過上方那樣設置參數的話,不是很好。下面修改一下設置參數的方式。
- 使用字典來傳遞參數
from flask import Flask,render_template # 創建Flask的app應用 app = Flask(__name__) # index視圖函數 @app.route("/index") def index(): # 使用字典的方式傳遞參數 context = { "user_name": 'libai', "age": 18, } return render_template('index.html', **context ) if __name__ == '__main__': app.run(debug=True)
- 再次訪問index

可以從上面看到,字典參數的傳遞是採用**context
的方式。
所以 **context
等價於 user_name=libai, age=18
的參數傳遞。
模板變數
在模板中{{ variable }}
結構表示變數,是一種特殊的佔位符,告訴模板引擎這個位置的值,從渲染模板時使用的數據中獲取;Jinja2除了能識別基本類型的變數,還能識別{}; 其中模板變數可以傳遞字典dict
,列表list
,下面再來寫幾個複雜一些的參數傳遞示例。
1. 編寫視圖函數index,設置多幾個類型參數
# index視圖函數 @app.route("/index") def index(): # 使用字典的方式傳遞參數 context = { "user_name": 'libai', "age": 18, # 傳遞字典 "my_dict" : { "key1" : "value1", "key2" : "value2", }, # 傳遞list "my_list" : [1,2,3,4,5,6], # 傳遞int類型 "index" : 0 } return render_template('index.html', **context )
2. 編寫index.html,設置多幾個參數
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h3>name = {{ user_name }}</h3> <h3>age = {{ age }}</h3> <!-- 使用字典參數 --> <p>my_dict key1 = {{ my_dict["key1"] }}</p> <p>my_dict key2 = {{ my_dict.key2 }}</p> <!-- 使用列表list參數 --> <p>my_list = {{ my_list }}</p> <p>my_list[1] = {{ my_list[1] }}</p> <p>my_list[0] = {{ my_list[0] }}</p> <p>my_list[index] = {{ my_list[index] }}</p> </body> </html>
3. 訪問index

可以看到不管是dict
還是list
類型,都可以使用。
注意:在Django中模板中的變數是無法直接相加等運算操作的,而Flask調用的模板可以。
4. 設置模板變數執行運算
<!-- 變數運算 --> <p>my_list[1] = {{ my_list[1] }}</p> <p>my_list[2] = {{ my_list[2] }}</p> <p>my_list[1] + my_list[2] = {{ my_list[1] + my_list[2] }}</p> <p>my_list[1] - my_list[2] = {{ my_list[1] - my_list[2] }}</p> <p>my_list[1] * my_list[2] = {{ my_list[1] * my_list[2] }}</p> <p>my_list[1] / my_list[2] = {{ my_list[1] / my_list[2] }}</p> <p>my_list[1] % my_list[2] = {{ my_list[1] % my_list[2] }}</p>
5. 訪問index,查看運算結果

可以看出基本運算都可以執行。
過濾器
字元串過濾器
safe:禁用轉義;
<p>{{ '<em>hello</em>' | safe }}</p>
capitalize:把變數值的首字母轉成大寫,其餘字母轉小寫;
<p>{{ 'hello' | capitalize }}</p>
lower:把值轉成小寫;
<p>{{ 'HELLO' | lower }}</p>
upper:把值轉成大寫;
<p>{{ 'hello' | upper }}</p>
title:把值中的每個單詞的首字母都轉成大寫;
<p>{{ 'hello' | title }}</p>
trim:把值的首尾空格去掉;
<p>{{ ' hello world ' | trim }}</p>
reverse:字元串反轉;
<p>{{ 'olleh' | reverse }}</p>
format:格式化輸出;
<p>{{ '%s is %d' | format('name',17) }}</p>
striptags:渲染之前把值中所有的HTML標籤都刪掉;
<p>{{ '<em>hello</em>' | striptags }}</p>
支援鏈式使用過濾器
<p>{{ " hello world " | trim | upper }}</p>
可以多次使用過濾器。
列表過濾器
first:取第一個元素
<p>{{ [1,2,3,4,5,6] | first }}</p>
last:取最後一個元素
<p>{{ [1,2,3,4,5,6] | last }}</p>
length:獲取列表長度
<p>{{ [1,2,3,4,5,6] | length }}</p>
sum:列表求和
<p>{{ [1,2,3,4,5,6] | sum }}</p>
sort:列表排序
<p>{{ [6,2,3,1,5,4] | sort }}</p>
下面執行一下上面的過濾器,如下:


上面展示禁用轉義safe過濾器有些簡單,下面再來一個xss攻擊的示例。
xss攻擊示例
編寫一個輸入框,允許將輸入內容直接在頁面展示,查看是否會執行js程式碼。
1. 編寫xss.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <form action="/xss_ex" method="post"> <label for="textarea"></label><textarea name="textarea" id="textarea" cols="30" rows="10"></textarea> <input type="submit" value="提交"> </form> {{ textarea }} </body> </html>
2.編寫處理xss的視圖函數
# xss視圖函數 @app.route("/xss_ex",methods=['GET','POST']) def xss_ex(): textarea = "" if request.method == 'POST': textarea = request.form.get("textarea") return render_template('xss.html', textarea=textarea)
3.輸入js內容,查看是否會執行js
輸入<script>alert("hello");</script>
,點擊提交


可以看到默認js程式碼並不會被執行,而是會被轉義顯示字元串。那麼如果需要執行呢?
這時候就可以增加過濾器safe了。
4.設置禁用轉義過濾器safe

5.再次輸入內容js

此時將會執行js,如果這段js是惡意程式碼,那麼就是典型的xss攻擊!
語句塊過濾(不常用):
{% filter upper %} this is a Flask Jinja2 introduction {% endfilter %}
自定義過濾器
過濾器的本質是函數。當模板內置的過濾器不能滿足需求,可以自定義過濾器。自定義過濾器有兩種實現方式:一種是通過Flask應用對象的add_template_filter方法。還可以通過裝飾器來實現自定義過濾器。
自定義的過濾器名稱如果和內置的過濾器重名,會覆蓋內置的過濾器。
實現方式一:通過調用應用程式實例的add_template_filter方法實現自定義過濾器。該方法第一個參數是函數名,第二個參數是自定義的過濾器名稱。
def filter_double_sort(ls): return ls[::2] app.add_template_filter(filter_double_sort,'ls_2')
實現方式二:用裝飾器來實現自定義過濾器。裝飾器傳入的參數是自定義的過濾器名稱。
@app.template_filter('ls3') def filter_double_sort(ls): return ls[::-3]
上面則是自定義過濾器的兩種方式,下面來執行一下這兩個自定義過濾器的示例。
1.設置兩個自定義過濾器
# 自定義過濾器:方法一 def filter_double_sort(ls): return ls[::2] app.add_template_filter(filter_double_sort,'ls_2') # 自定義過濾器:方法二 @app.template_filter('ls3') def filter_double_sort(ls): return ls[::-3] @app.route("/index") def index(): # 使用字典的方式傳遞參數 context = { # 傳遞list "my_list" : [1,2,3,4,5,6], } return render_template('index.html', **context ) if __name__ == '__main__': app.run(debug=True)
2.在index.html使用兩個自定義過濾器
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> 自定義過濾器: <p>{{ my_list | ls_2 }}</p> <p>{{ my_list | ls3 }}</p> </body> </html>
3.頁面顯示如下
