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>

下面來寫一個簡單的示例:

  1. 導入Flask使用模板的方法
from flask import render_template
  1. 在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>
  1. 編寫使用模板的示例程式碼
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)
  1. 測試訪問視圖

http://127.0.0.1:5000/index

可以看到,模板成功顯示出了參數內容。但是通過上方那樣設置參數的話,不是很好。下面修改一下設置參數的方式。

  1. 使用字典來傳遞參數
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)
  1. 再次訪問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.頁面顯示如下