Flask模板渲染
Jinja2模板引擎簡介
模板
視圖函數的主要作用是生成請求的響應,這是最簡單請求.實際上,視圖函數有兩個作用:
- 處理業務邏輯
- 返回響應內容
在大型應用中,把業務邏輯和表現內容放在一起,會增加代碼的復雜度和維護成本.
- 模板其實是一個包含響應文本的文件,其中用占位符(變量)表示動態部分,告訴模板引擎其具體的值需要從使用的數據中獲取
- 使用真實值替換變量,再返回最終得到的字符串,這個過程稱為'渲染'
- Flask是使用Jinja2這個模板引擎來渲染模板
使用模板的好處
- 視圖函數只負責業務邏輯和數據處理(業務邏輯方面)
- 而模板則取到視圖函數的數據結果進行展示(試圖展示方面)
- 代碼結構清晰,耦合度低
Jinja2
兩個概念
- Jinja2:是Python下一個被廣泛應用的模板引擎,是由Python實現的模板語言,他的設計思想來源於Django的模板引擎,並擴展了其語法和一系列強大的功能,其是Flask內置的模板語言
- 模板語言:是一種被設計來自動生成文檔的簡單文本格式,在模板語言中,一般都會把一些變量傳給模板,替換模板的特定位置上預先定義好的占位變量名
渲染模板函數
- Flask提供的render_template函數封裝了該模板引擎
- render_template函數的第一個參數是模板的文件名,后面的參數都是鍵值對,表示模板中變量對應的真實值
模板變量
變量
-
代碼中傳入字符串,列表,字典到模板中
@app.route('/') def index(): # 往模板中傳入的數據 my_str = 'Hello Word' # 字符串 my_int = 10 # 數字 my_array = [3, 4, 2, 1, 7, 9] # 列表 my_dict = { # 字典 'name': 'xiaoming', 'age': 18, 'weight': 125, } return render_template('temp_demo1.html', my_str=my_str, my_int=my_int, my_array=my_array, my_dict=my_dict )
-
模板中代碼
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> 我的模板html內容 <br/>{{ my_str }} <br/>{{ my_int }} <br/>{{ my_array }} <br/>{{ my_dict }} </body> </html> <!-- 運行結果: 我的模板html內容 Hello Word 10 [3, 4, 2, 1, 7, 9] {'name': 'xiaoming','age': 18,'weight': 125} -->
-
列表、字典的取值運算
<!--算數運算--> <br/> my_int + 10 的和為:{{ my_int + 10 }} <!--列表取值 1. list[index] 2. list.index --> <br/> my_int + my_array第0個值的和為:{{ my_int + my_array[0] }} <br/> my_array 第0個值為:{{ my_array[0] }} <br/> my_array 第1個值為:{{ my_array.1 }} <!--字典取值 1. dict.key 2. dice[key] 3. dict.get(key) --> <br/> my_dict 中 name 的值為:{{ my_dict['name'] }} <br/> my_dict 中 age 的值為:{{ my_dict.age }} <br/> my_dict 中 weight 的值為:{{ my_dict.get('weight') }} <!-- 運行結果: my_int + 10 的和為:20 my_int + my_array第0個值的和為:13 my_array 第0個值為:3 my_array 第1個值為:4 my_dict 中 name 的值為:xiaoming my_dict 中 age 的值為:18 my_dict 中 weight 的值為:125 -->
控制結構
條件控制語句
Jinja2語法中的if語句跟Python中的if語句相似,后面的布爾值或者返回布爾值的表達式將決定代碼中那個流程會被執行:
{% if user %}
Hello, {{ user }}
{% else %}
Hello, Stranger!
{% endif %}
for循環
我們可以在Jinja2中使用循環來迭代任何列表或者生成器函數
<ul>
{% for comment in comment %}
<li>{{ comment }}</li>
{% endfor %}
</ul>
循環和if語句可以組合使用
{% for post in posts if post.text %}
<div>
<h1>{{ post.title }}</h1>
<p>{{ post.text | safe }}</p>
</div>
{% endfor %}
在一個for循環塊中你可以訪問這些特殊的變量
變量 | 描述 |
---|---|
loop.inde | 當前循環迭代的次數(從1開始) |
loop.index0 | 當前循環迭代的次數(從0開始) |
loop.revindex | 到循環結束需要迭代的次數(從1開始) |
loop.revindex0 | 到循環結束需要迭代的次數(從0開始) |
loop.frist | 如果是第一次迭代,為True |
loop.last | 如果是最后一次迭代,為True |
loop.length | 序列中的項目數 |
loop.cycle | 在一串序列間期取值的輔助函數 |
宏,類似Python代碼中的函數
定義宏
{% macro input() %}
<input type="text"
name="username"
value=""
size="30"/>
{% endmacro %}
調用宏
{{ input() }}
定義帶參數的宏
{% macro input(name,value='',type='text',size=20) %}
<input type="{{ type }}"
name="{{ name }}"
value="{{ value }}"
size="{{ size }}"/>
{% endmacro %}
調用宏,並傳遞參數
{{ input(value='name',type='password',size=40)}}
把宏單獨抽取出來,封裝成html文件,其它模板中導入使用
文件名可以自定義macro.html
{% macro function() %}
<input type="text" name="username" placeholde="Username">
<input type="password" name="password" placeholde="Password">
<input type="submit">
{% endmacro %}
在其它模板文件中先導入,再調用
{% import 'macro.html' as func %}
{% func.function() %}
模板繼承
在模板中,可能會遇到以下情況:
- 多個模板具有完全相同的頂部和底部內容
- 多個模板中具有相同的模板代碼內容,但是內容中部分值不同
- 多個模板中具有完全相同的html代碼塊內容
像遇到這種情況,可以使用Jinja2模板中的繼承來進行實現
模板繼承是為了重用模板中的公共內容.一般Web開發中,繼承主要使用在網站的頂部菜單,底部.這些內容可以定義在父模板中,子模板直接繼承,不需要重寫
定義標簽的內容
{% block top %}
{% endblock%}
- 相當於在父模板中挖個坑,當子模板繼承父模板時候,可以進行填充
- 子模板使用extends指令聲明這個模板繼承自哪個模板
- 父模板中定義的塊在子模板中被重新定義,在子模板中調用父模板的內容可以使用super()
from flask import Flask,render_template
app = Flask(__name__)
@app.route('/parent')
def parent():
return render_template('parent.html')
@app.route('/child')
def child():
return render_template('child.html')
if __name__ == '__main__':
app.run(debug=True)
父模板:parent.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h2>我是頭部</h2>
<hr>
{# 將父模板中需要重寫的內容使用block包含起來 #}
{% block content %}
<h2>我是父模板內容</h2>
{% endblock %}
<hr>
<h2>我是底部</h2>
</body>
</html>
子模板:child.html
{# 1 使用extends關鍵字集成模板 #}
{% extends '06_one.html' %}
{# 重寫block #}
{% block content %}
<h2>我是子模板內容</h2>
{% endblock %}
- 模板繼承使用時注意點:
- 不支持多繼承。
- 為了便於閱讀,在子模板中使用extends時,盡量寫在模板的第一行。
- 不能在一個模板文件中定義多個相同名字的block標簽。
- 當在頁面中使用多個block標簽時,建議給結束標簽起個名字,當多個block嵌套時,閱讀性更好。
包含(Include)
Jinja2模板中,除了宏和繼承,還支持一種代碼重用的功能,叫包含(Include)。它的功能是將另一個模板整個加載到當前模板中,並直接渲染。
示例:
include的使用
{% include 'hello.html' %}
包含在使用時,如果包含的模板文件不存在時,程序會拋出TemplateNotFound異常,可以加上ignore missing關鍵字。如果包含的模板文件不存在,會忽略這條include語句。
示例:
include的使用加上關鍵字ignore missing
{% include 'hello.html' ignore missing %}
- 宏、繼承、包含:
- 宏(Macro)、繼承(Block)、包含(include)均能實現代碼的復用。
- 繼承(Block)的本質是代碼替換,一般用來實現多個頁面中重復不變的區域。
- 宏(Macro)的功能類似函數,可以傳入參數,需要定義、調用。
- 包含(include)是直接將目標模板文件整個渲染出來。
過濾器
過濾器本質就是函數。
有時候我們不僅僅只是需要輸出變量的值,我們還需要修改變量的顯示,甚至格式化、運算等等,而在模板中是不能直接調用Python中的某些方法,那么這就用到了過濾器
使用方法:
- 過濾器的使用方法為:變量名 | 過濾器.
- {{ variable | filter_name(*args)}}
- 如果沒有任何參數傳給過濾器,則可以把括號省略掉
- {{variable | filter_name}}
- 如:'',這個過濾器的作用:把變量variable的值首字母首字母轉化為大寫,其余字母轉化為小寫
鏈式調用
在Jinja2中,過濾器支持鏈式調用,示例如下:
{{'hello word' | reverse | upper}}
輸出結果為:
DROW OLLEH
常見內建過濾器
-
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>
-
reverse:字符串反轉
<p>{{ 'olleh' | reverse }}</p>
-
format:格式化輸出
<p>{{ '%s is %d' | format('name',17) }}</p>
-
striptags:渲染之前把值中所有的HTML標簽都刪掉
<p>{{ '<em>hello</em>' | striptags }}</p>
-
truncate: 字符串截斷
<p>{{ 'hello every one' | truncate(9)}}</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>
語句塊過濾
{% filter upper %}
#一大堆文字#
{% endfilter %}
自定義過濾器
過濾器本質是函數,當模板內置的過濾器不能滿足需求,可以自定義過濾器。自定義過濾器有兩種方式實現。
- 一種是通過Flask應用對象的add_template_filter方法
- 通過裝飾器來實現自定義過濾器
重要:自定義過濾器名稱如果和內置過濾器重名,會覆蓋內置的過濾器
需求:添加列表反轉的過濾器
方式一
通過調用應用程序實例的 add_template_filter 方法實現自定義過濾器。該方法第一個參數是函數名,第二個參數是自定義的過濾器名稱:
def do_listreverse(li):
# 通過原列表創建一個新列表
temp_li = list(li)
# 將新列表進行返轉
temp_li.reverse()
return temp_li
app.add_template_filter(do_listreverse,'lireverse')
方式二
用裝飾器來實現自定義過濾器。裝飾器傳入的參數是自定義的過濾器名稱。
@app.template_filter('lireverse')
def do_listreverse(li):
# 通過原列表創建一個新列表
temp_li = list(li)
# 將新列表進行返轉
temp_li.reverse()
return temp_li
在html中使用該自定義過濾器
<br/> my_array 原內容:{{ my_array }}
<br/> my_array 反轉:{{ my_array | lireverse }}
<!--
運行結果:
my_array 原內容:[3, 4, 2, 1, 7, 9]
my_array 反轉:[9, 7, 1, 2, 4, 3]
-->
完整代碼:
from flask import Flask, render_template
app = Flask(__name__)
# 1.自定義列表反轉函數
@app.template_filter('list_reverse')
# 方法二:使用裝飾器
def list_reverse(list):
# list = []
list.reverse()
return list
# 2.將自定義的函數添加到flask過濾器中
app.add_template_filter(list_reverse, 'list_reverse')
@app.route('/')
def index():
list = [1, 3, 4, 5, 2] # 2,5,4,3,1
return render_template('test.html', list=list)
if __name__ == '__main__':
app.run(debug=True)
<!--test.html-->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h2>自定義過濾器</h2>
<br>
{{ list }}
<br>
{{ list | list_reverse }}
</body>
</html>