在客戶端和服務器交互的過程當中,有些準備工做或掃尾工做須要處理,好比: - 在請求開始時,創建數據庫鏈接; - 在請求開始時,根據需求進行權限校驗; - 在請求結束時,指定數據的交互格式; 爲了讓每一個視圖函數避免編寫重複功能的代碼,Flask提供了通用設施的功能,即請求鉤子。 請求鉤子是經過裝飾器的形式實現,Flask支持以下四種請求鉤子: - before_first_request - 在處理第一個請求前執行(初始化的時候) - before_request - 在每次請求前執行 - 若是在某修飾的函數中返回了一個響應,視圖函數將再也不被調用 - after_request - 前提是視圖函數沒有拋出錯誤,在每次請求視圖函數處理以後執行 - 接受一個參數:視圖函數做出的響應 - 在此函數中能夠對響應值在返回以前作最後一步修改處理 - 須要將參數中的響應在此參數中進行返回 - teardown_request: - 在每次請求後執行,不管視圖函數是否正常工做 - 接受一個參數:錯誤信息,若是有相關錯誤拋出
工做模式爲非調試模式,即Debug=False
代碼:html
#!/usr/bin/env python # -*- coding: utf-8 -*- #author tom from flask import Flask from settings.dev import DevConfig app=Flask(__name__) #配置 app.config.from_object(DevConfig) @app.before_first_request def before_first_request(): print('----before_first_request------') print('系統初始化的時候運行這個鉤子方法') print('只會在接收到第一個客戶端請求時,執行這個代碼') @app.before_request def before_request(): print('---before_request--') print('每一次收到客戶端的時候,都會執行這個鉤子') print('通常用來判斷權限,或者轉換路由參數或者預處理客戶端請求的數據') #須要接受一個參數 response @app.after_request def after_request(response): print('----after_request----') print('在處理完請求後,會執行這個方法') print('通常能夠用於記錄會員/管理員的操做歷史,瀏覽歷史,清理收尾工做') response.headers['content-Type']='application/json' #必須返回response參數 return response
@app.teardown_request def teardown_request(exc): print('----teardown_request-----') print('在每一次請求之後,執行這個鉤子方法,若是有異常錯誤,則會傳遞錯誤異常對象到當前方法參數中') print(exc)
#能夠在請求以後作相應的處理
path=request.path
if path ==url_for("index"):
print("在訪問index視圖函數以後要作什麼響應的處理")
elif path==url_for("login"):
pritn("在訪問login函數以後作什麼樣的處理")
#另一種方式
if path in [url_for('index'),url_for('login')]:
print('作相應的處理')
#必須返回response參數
return response
@app.route('/') def index(): print('--視圖函數--') print('視圖函數運行了') return '視圖函數被運行了<br>' if __name__ == '__main__': app.run(host='0.0.0.0',port=80)
第一次請求j結果:前端
----before_first_request---- 系統初始化的時候,執行這個鉤子方法 會在接收到第一個客戶端請求時,執行這裏的代碼 ----before_request---- 每一次接收到客戶端請求時,執行這個鉤子方法 通常能夠用來判斷權限,或者轉換路由參數或者預處理客戶端請求的數據 ----視圖函數---- 視圖函數被運行了 ----after_request---- 在處理請求之後,執行這個鉤子方法 通常能夠用於記錄會員/管理員的操做歷史,瀏覽歷史,清理收尾的工做 ----teardown_request---- 在每一次請求之後,執行這個鉤子方法,若是有異常錯誤,則會傳遞錯誤異常對象到當前方法的參數中 None
第二次請求:python
----before_request---- 127.0.0.1 - - [08/Apr/2019 09:23:53] "GET / HTTP/1.1" 200 - 每一次接收到客戶端請求時,執行這個鉤子方法 通常能夠用來判斷權限,或者轉換路由參數或者預處理客戶端請求的數據 ----視圖函數---- 視圖函數被運行了 ----after_request---- 在處理請求之後,執行這個鉤子方法 通常能夠用於記錄會員/管理員的操做歷史,瀏覽歷史,清理收尾的工做 ----teardown_request---- 在每一次請求之後,執行這個鉤子方法,若是有異常錯誤,則會傳遞錯誤異常對象到當前方法的參數中 None
- abort 方法 - 拋出一個給定狀態代碼的 HTTPException 或者 指定響應,例如想要用一個頁面未找到異常來終止請求,你能夠調用 abort(404)。 - 參數: - code – HTTP的錯誤狀態碼(必須是標準的狀態碼)
abort(404)
abort(500)
#自定義異常
resp=response('登陸失敗')
abort(resp)
- errorhandler 裝飾器 - 註冊一個錯誤處理程序,當程序拋出指定錯誤狀態碼的時候,就會調用該裝飾器所裝飾的方法 - 參數: - code_or_exception – HTTP的錯誤狀態碼或指定異常 - 例如統一處理狀態碼爲500的錯誤給用戶友好的提示:
@app.errorhandler(500)
def internal_server_error(e):
return '服務器搬家了'sql
捕獲指定異常數據庫
@app.errorhandler(ZeroDivisionError)django
def zero_division_error(e):
return '除數不能爲0'json
代碼:flask
from flask import Flask,abort from settings.dev import DevConfig app=Flask(__name__) #配置 app.config.from_object(DevConfig) @app.errorhandler(500) def internal_server_error(e): return '<h1>服務器搬家了</h1>' @app.errorhandler(Exception) def zero_division_Error(e): return '除數不能爲0' @app.route('/') def index(): print('--視圖函數--') print('視圖函數運行了') # raise Exception('異常了') # abort(500) abort(404) return '視圖函數被運行了<br>' if __name__ == '__main__': app.run(host='0.0.0.0',port=80)
上下文:即語境,語意,在程序中能夠理解爲在代碼執行到某一時刻時,根據以前代碼所作的操做以及下文即將要執行的邏輯,能夠決定在當前時刻下可使用到的變量,或者能夠完成的事情。 Flask中有兩種上下文,請求上下文(request context)和應用上下文(application context)。 Flask中上下文對象:至關於一個容器,保存了 Flask 程序運行過程當中的一些信息。 1. *application* 指的就是當你調用`app = Flask(__name__)`建立的這個對象`app`; 2. *request* 指的是每次`http`請求發生時,`WSGI server`(好比gunicorn)調用`Flask.__call__()`以後,在`Flask`對象內部建立的`Request`對象; 3. *application* 表示用於響應WSGI請求的應用自己,*request* 表示每次http請求; 4. *application*的生命週期大於*request*,一個*application*存活期間,可能發生屢次http請求,因此,也就會有多個*request*
思考:在視圖函數中,如何取到當前請求的相關數據?好比:請求地址,請求方式,cookie等等 在 flask 中,能夠直接在視圖函數中使用 **request** 這個對象進行獲取相關數據,而 **request** 就是請求上下文的對象,
保存了當前本次請求的相關數據,請求上下文對象有:request、session - request - 封裝了HTTP請求的內容,針對的是http請求。舉例:user = request.args.get('user'),獲取的是get請求的參數。 - session - 用來記錄請求會話中的 :session['name'] = user.id,能夠記錄用戶信息。還能夠經過session.get('name')獲取用戶信息。
它的字面意思是 應用上下文,但它不是一直存在的,它只是request context 中的一個對 app 的代理(人),
所謂local proxy。它的做用主要是幫助 request 獲取當前的應用,它是伴 request 而生,隨 request 而滅的。
應用上下文對象有:current_app,g
應用的啓動腳本是哪一個文件,啓動時指定了哪些參數api
加載了哪些配置文件,導入了哪些配置安全
鏈接了哪一個數據庫
有哪些能夠調用的工具類、常量
當前flask應用在哪一個機器上,哪一個IP上運行,內存多大
current_app.name
current_app.test_value='value'
g 做爲 flask 程序全局的一個臨時變量,充當者中間媒介的做用,咱們能夠經過它傳遞一些數據,g 保存的是當前請求的全局變量,不一樣的請求會有不一樣的全局變量,經過不一樣的thread id區別
g.name='abc'
注意:不一樣的請求,會有不一樣的全局變量
請求上下文:保存了客戶端和服務器交互的數據
應用上下文:flask 應用程序運行過程當中,保存的一些配置信息,好比程序名、數據庫鏈接、應用信息等
代碼:
#!/usr/bin/env python # -*- coding: utf-8 -*- #author tom from flask import Flask,current_app,g from settings.dev import DevConfig application=Flask(__name__) #配置 application.config.from_object(DevConfig) @application.after_request def after_request(response): print('%s訪問了Home'%g.name) return response @application.route('/home') def home(): current_app.test_value='dajiba' g.name='xiaojiba'#只能在當前情趣中獲取定義的數據 return '--home-' @application.route('/') def index(): print(current_app.config) print(current_app.name) print(current_app.test_value) return '視圖函數被運行了<br>' if __name__ == '__main__': application.run(host='0.0.0.0',port=80)
安裝: pip install flask-script
集成 Flask-Script到flask應用中: #!/usr/bin/env python # -*- coding: utf-8 -*- #author tom from flask import Flask from settings.dev import DevConfig from flask_script import Manager,Command app=Flask(__name__) # 把 Manager 類和應用程序實例進行關聯 manage=Manager(app) @app.route('/') def index(): return '視圖函數被運行了<br>' class Hello(Command): '''自定義命令類''' def run(self): print('Hello 執行了') manage.add_command('test',Hello()) if __name__ == '__main__': manage.run()
Flask內置的模板語言,它的設計思想來源於 Django 的模板引擎,並擴展了其語法和一系列強大的功能。 渲染模版函數 - Flask提供的 ender_template函數封裝了該模板引擎 - render_template 函數的第一個參數是模板的文件名,後面的參數都是鍵值對,表示模板中變量對應的真實值。
1. 在視圖函數中設置渲染模板 @app.route('/') def index(): return render_template('index.html')
1. 在項目下建立 `templates` 文件夾,用於存放全部的模板文件,並在目錄下建立一個模板html文件 `index.html` <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> 個人模板html內容 </body> </html> {{}} 來表示變量名,這種 {{}} 語法叫作**變量代碼塊**<h1>{{ post.title }}</h1> Jinja2 模版中的變量代碼塊能夠是任意 Python 類型或者對象,只要它可以被 Python 的 str() 方法轉換爲一個字符串就能夠,比
如,能夠經過下面的方式顯示一個字典或者列表中的某個元素: {{your_dict['key']}} {{your_list[0]}} 用 {**%%**} 定義的控制代碼塊,能夠實現一些語言層次的功能,好比循環或者if語句 {% if user %} {{ user }} {% else %} hello! <ul> {% for index in indexs %} <li> {{ index }} </li> {% endfor %} </ul> ``` 使用 {# #} 進行註釋,註釋的內容不會在html中被渲染出來 {# {{ name }} #}## 模板中特有的變量和函數 你能夠在本身的模板中訪問一些 Flask 默認內置的函數和對象 #### request 就是flask中表明當前請求的request對象: {{request.url}} http://127.0.0.1#### session 爲Flask的session對象 {{session.new}} True #### g變量 在視圖函數中設置g變量的 name 屬性的值,而後在模板中直接能夠取出 {{ g.name }} #### url_for() url_for會根據傳入的路由器函數名,返回該路由對應的URL,在模板中始終使用url_for()就能夠安全的修改路由綁定的URL,則不比擔憂模板中渲染出錯的連接: {{url_for('home')}} 若是咱們定義的路由URL是帶有參數的,則能夠把它們做爲關鍵字參數傳入url_for(),Flask會把他們填充進最終生成的URL中: {{ url_for('post', post_id=1)}} /post/1## 流程控制 主要包含兩個: - if/else if /else / endif - for / endfor ### if語句 Jinja2 語法中的if語句跟 Python 中的 if 語句類似,後面的布爾值或返回布爾值的表達式將決定代碼中的哪一個流程會被執行: {%if user.is_logged_in() %} <a href='/logout'>Logout</a> {% else %} <a href='/login'>Login</a> {% endif %} 過濾器能夠被用在 if 語句中: {% if comments | length > 0 %} There are {{ comments | length }} comments {% else %} There are no comments {% endif %} ### 循環語句 - 咱們能夠在 Jinja2 中使用循環來迭代任何列表或者生成器函數 {% for post in posts %} <div> <h1>{{ post.title }}</h1> <p>{{ post.text | safe }}</p> </div> {% endfor %}
- 循環和if語句能夠組合使用,以模擬 Python 循環中的 continue 功能,下面這個循環將只會渲染post.text不爲None的那些post: ```python {% for post in posts if post.text %} <div> <h1>{{ post.title }}</h1> <p>{{ post.text | safe }}</p> </div> {% endfor %} - 在一個 for 循環塊中你能夠訪問這些特殊的變量:
在視圖函數中設置渲染模板
@app.route('/')
def index():
return render_template('index.html')
在項目下建立 templates
文件夾,用於存放全部的模板文件,並在目錄下建立一個模板html文件 index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
個人模板html內容
</body>
</html>
{{}} 來表示變量名,這種 {{}} 語法叫作變量代碼塊
<h1>{{ post.title }}</h1>
Jinja2 模版中的變量代碼塊能夠是任意 Python 類型或者對象,只要它可以被 Python 的 str() 方法轉換爲一個字符串就能夠,好比,能夠經過下面的方式顯示一個字典或者列表中的某個元素:
{{your_dict['key']}}
{{your_list[0]}}
用 {%%} 定義的控制代碼塊,能夠實現一些語言層次的功能,好比循環或者if語句
{% if user %}
{{ user }}
{% else %}
hello!
<ul>
{% for index in indexs %}
<li> {{ index }} </li>
{% endfor %}
</ul>
使用 {# #} 進行註釋,註釋的內容不會在html中被渲染出來
{# {{ name }} #}
你能夠在本身的模板中訪問一些 Flask 默認內置的函數和對象
你能夠從模板中直接訪問Flask當前的config對象:
{{config.SQLALCHEMY_DATABASE_URI}}
sqlite:///database.db
就是flask中表明當前請求的request對象:
{{request.url}}
http://127.0.0.1
爲Flask的session對象
{{session.new}}
True
在視圖函數中設置g變量的 name 屬性的值,而後在模板中直接能夠取出
{{ g.name }}
url_for會根據傳入的路由器函數名,返回該路由對應的URL,在模板中始終使用url_for()就能夠安全的修改路由綁定的URL,則不比擔憂模板中渲染出錯的連接:
{{url_for('home')}}
若是咱們定義的路由URL是帶有參數的,則能夠把它們做爲關鍵字參數傳入url_for(),Flask會把他們填充進最終生成的URL中:
{{ url_for('post', post_id=1)}}
/post/1
主要包含兩個:
- if/else if /else / endif
- for / endfor
Jinja2 語法中的if語句跟 Python 中的 if 語句類似,後面的布爾值或返回布爾值的表達式將決定代碼中的哪一個流程會被執行:
{%if user.is_logged_in() %}
<a href='/logout'>Logout</a>
{% else %}
<a href='/login'>Login</a>
{% endif %}
過濾器能夠被用在 if 語句中:
{% if comments | length > 0 %}
There are {{ comments | length }} comments
{% else %}
There are no comments
{% endif %}
咱們能夠在 Jinja2 中使用循環來迭代任何列表或者生成器函數
{% for post in posts %}
<div>
<h1>{{ post.title }}</h1>
<p>{{ post.text | safe }}</p>
</div>
{% endfor %}
循環和if語句能夠組合使用,以模擬 Python 循環中的 continue 功能,下面這個循環將只會渲染post.text不爲None的那些post:
{% for post in posts if post.text %}
<div>
<h1>{{ post.title }}</h1>
<p>{{ post.text | safe }}</p>
</div>
{% endfor %}
在一個 for 循環塊中你能夠訪問這些特殊的變量:
變量 | 描述 |
---|---|
loop.index | 當前循環迭代的次數(從 1 開始) |
loop.index0 | 當前循環迭代的次數(從 0 開始) |
loop.revindex | 到循環結束須要迭代的次數(從 1 開始) |
loop.revindex0 | 到循環結束須要迭代的次數(從 0 開始) |
loop.first | 若是是第一次迭代,爲 True 。 |
loop.last | 若是是最後一次迭代,爲 True 。 |
loop.length | 序列中的項目數。 |
loop.cycle | 在一串序列間期取值的輔助函數。見下面示例程序。 |
在循環內部,你可使用一個叫作loop的特殊變量來得到關於for循環的一些信息
好比:要是咱們想知道當前被迭代的元素序號,並模擬Python中的enumerate函數作的事情,則可使用loop變量的index屬性,例如:
{% for post in posts%}
{{loop.index}}, {{post.title}}
{% endfor %}
會輸出這樣的結果
1, Post title
2, Second Post
cycle函數會在每次循環的時候,返回其參數中的下一個元素,能夠拿上面的例子來講明:
{% for post in posts%}
{{loop.cycle('odd','even')}} {{post.title}}
{% endfor %}
會輸出這樣的結果:
odd Post Title
even Second Post
- 在循環內部,你可使用一個叫作loop的特殊變量來得到關於for循環的一些信息
- 好比:要是咱們想知道當前被迭代的元素序號,並模擬Python中的enumerate函數作的事情,則可使用loop變量的index屬性,
例如:
{% for post in posts%}
{{loop.index}},
{{post.title}}
{% endfor %}
- 會輸出這樣的結果 1, Post title 2, Second Post
- cycle函數會在每次循環的時候,返回其參數中的下一個元素,能夠拿上面的例子來講明:
{% for post in posts%}
{{loop.cycle('odd','even')}}
{{post.title}} {% endfor %}
- 會輸出這樣的結果: ```python odd Post Title even Second Post ```
代碼:
#!/usr/bin/env python # -*- coding: utf-8 -*- #author tom from flask import Flask,render_template,g from settings.dev import DevConfig from flask_script import Manager,Command app=Flask(__name__,template_folder='templates') # 把 Manager 類和應用程序實例進行關聯 app.config.from_object(DevConfig) def func(): return 'hello world' @app.route('/') def index(): dict1={ 'name':'haha', 'age':18, 'sex':'2' } list1=['張三','李四','王五'] g.name='wahahhahah' return render_template('index.html',title='個人flask網頁',dict1=dict1,list1=list1,func=func) if __name__ == '__main__': app.run()
index.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>{{title}}</title> </head> <body> <h2>輸出變量</h2> <p>視圖中的定義的變量</p> {{title}} <p>輸出全局變量http://127.0.0.1?username=xaioming</p> {{request.args.username}} {{g.name}} <hr> {{dict1.name}} {{dict1['name']}} {{dict1.get('name')}} <hr> {{list1.0}} {{list1.1}} <p>直接簡單元素調用或者調用全局函數</p> {{1+1}} {{func()}} <hr> <h2>控制語句</h2> <p>if語句</p> 小明性別是: <!--這是html的註釋--> {#這個是jinja2的模板註釋#} #這不是python文件,因此這不是註釋 {% if dict1.sex == 1 %} <span>男</span> {% elif dict1.sex == 3 %} <span>保密</span> {% else %} <span>女</span> {% endif %} <!--記得結束。endif--> <p>for循環</p> <table border="1" width="600" algin="center"> {% for item in list1 %} <tr> <td>{{ loop.index }}</td> <td>{{ loop.index0 }}</td> <td>{{ item }}</td> </tr> {% endfor %} </table> </body> </html>
過濾器的本質就是函數。有時候咱們不只僅只是須要輸出變量的值,咱們還須要修改變量的顯示,甚至格式化、運算等等,
而在模板中是不能直接調用 Python 中的某些方法,那麼這就用到了過濾器。 使用方式: - 過濾器的使用方式爲:變量名 | 過濾器。 {{variable | filter_name(*args)}} - 若是沒有任何參數傳給過濾器,則能夠把括號省略掉 {{variable | filter_name }} 在 jinja2 中,過濾器是能夠支持鏈式調用的,示例以下: {{ "hello world" | reverse | upper }} ### 常見的內建過濾器 #### 字符串操做 - 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:列表求和 ```python <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]
代碼:
#!/usr/bin/env python # -*- coding: utf-8 -*- #author tom from flask import Flask,render_template,g from settings.dev import DevConfig from flask_script import Manager,Command app=Flask(__name__,template_folder='templates') # 把 Manager 類和應用程序實例進行關聯 app.config.from_object(DevConfig) #自定義過濾器 def ftwo(string): return '%.2f'%string #把自定義法過濾器添加到裏面 app.add_template_filter(ftwo,'ft') @app.route('/home') def index(): return render_template('home.html',num=22.222222,title='hello quelijin') if __name__ == '__main__': app.run()
home.html
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> <head> <title>Title</title> </head> <body> {#自帶的過濾器#} {{ title|reverse|upper|truncate(6,True,'..',0) }} <hr> {{ num|ft }}元 </body> </html>
在模板中,可能會遇到如下狀況: - 多個模板具備徹底相同的頂部和底部內容 - 多個模板中具備相同的模板代碼內容,可是內容中部分值不同 - 多個模板中具備徹底相同的 html 代碼塊內容 像遇到這種狀況,可使用 JinJa2 模板中的 **繼承** 來進行實現 模板繼承是爲了重用模板中的公共內容。通常Web開發中,繼承主要使用在網站的頂部菜單、底部。這些內容能夠定義在父模板中,子模板直接繼承,而不須要重複書寫。 - 標籤訂義的內容 {% block top %} {% endblock %} - 至關於在父模板中挖個坑,當子模板繼承父模板時,能夠進行填充。 - 子模板使用 extends 指令聲明這個模板繼承自哪一個模板 - 父模板中定義的塊在子模板中被從新定義,在子模板中調用父模板的內容可使用super() 父模板代碼: base.html {% block top %} 頂部菜單 {% endblock top %} {% block content %} {% endblock content %} {% block bottom %} 底部 {% endblock bottom %} 子模板代碼: - extends指令聲明這個模板繼承自哪 {% extends 'base.html' %} {% block content %} 須要填充的內容 {% endblock content %} 模板繼承使用時注意點: 1. 不支持多繼承 2. 爲了便於閱讀,在子模板中使用extends時,儘可能寫在模板的第一行。 3. 不能在一個模板文件中定義多個相同名字的block標籤。 4. 當在頁面中使用多個block標籤時,建議給結束標籤起個名字,當多個block嵌套時,閱讀性更好。
和django模板繼承的不一樣
#include的使用 #把其餘模板直接導入並在當前頁面直接渲染 {%include 'hello.html'%}
#!/usr/bin/env python # -*- coding: utf-8 -*- #author tom from flask import Flask,render_template,g from settings.dev import DevConfig from flask_script import Manager,Command app=Flask(__name__,template_folder='templates') # 把 Manager 類和應用程序實例進行關聯 app.config.from_object(DevConfig) #自定義過濾器 @app.route('/') def index(): return render_template('first.html') @app.route('/list') def mylist(): return render_template('two.html') if __name__ == '__main__': app.run()
main.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h2>公共頭1</h2> {% block main %} {% endblock %} <h2>公共腳1</h2> </body> </html>
main2.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h2>公共頭2</h2> {% block main %} {% endblock %} <h2>公共腳2</h2> {% block myfoot %} {% endblock %} </body> </html>
frist.html
{% extends 'main.html' %}
{% block main %}
<h3>第一個頁面</h3>
{% endblock %}
two.html
{% extends 'main2.html' %} {% block main %} <h3>第二個頁面</h3> {% endblock %} {% block myfoot %} <span>能夠引入js腳本代碼</span> {% endblock %}
在 Flask 中, Flask-wtf 擴展有一套完善的 csrf 防禦體系,對於咱們開發者來講,使用起來很是簡單
pip install Flask-WTF
1. 設置應用程序的 secret_key,用於加密生成的 csrf_token 的值 # session加密的時候已經配置過了.若是沒有在配置項中設置,則以下: app.secret_key = "#此處能夠寫隨機字符串#" 1. 導入 flask_wtf.csrf 中的 CSRFProtect 類,進行初始化,並在初始化的時候關聯 app from flask.ext.wtf import CSRFProtect CSRFProtect(app)
注意:導入錯誤:應該安裝 pip install flask_wtf
導入:from flask_wtf import CSRFProtect
代碼:
#!/usr/bin/env python # -*- coding: utf-8 -*- #author tom from flask import Flask,render_template,g,request from settings.dev import DevConfig from flask_wtf import CSRFProtect app=Flask(__name__,template_folder='templates') # 把 Manager 類和應用程序實例進行關聯 app.config.from_object(DevConfig) #開啓csrf_token防範機制 CSRFProtect(app) @app.route('/',methods=['get','post']) def index(): if request.method=='GET': return render_template('form.html') else: print(request.form) return 'ok' if __name__ == '__main__': app.run()
在頁面加入csrf_token 令牌
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <form action="" method="post"> <input type="hidden" name="csrf_token" value="{{ csrf_token() }}"> 帳號: <input type="text" name="username"><br><br> 密碼: <input type="text" name="password"><br><br> <input type="submit" value="提交"> </form> </body> </html>
一個表單驗證的小例子
wtf_demo.py
#!/usr/bin/env python # -*- coding: utf-8 -*- # author tom from flask import Flask, url_for, session from flask import render_template from flask import redirect from flask_wtf import FlaskForm from wtforms import StringField from wtforms import PasswordField from wtforms import SubmitField from wtforms.validators import DataRequired from wtforms.validators import EqualTo app = Flask(__name__) app.config["SECRET_KEY"] = "adsdfgd34ergeddfg3534gr" # 定義表單的模型類 class RigsterForm(FlaskForm): """自定義的表單註冊模型類""" # 名字 驗證器 # DataRequired 保證數據必須填寫,而且可在括號內填寫不符合規矩的提示語 user_name = StringField(label=u"用戶名", validators=[DataRequired(u"用戶名不能爲空")]) user_ps = PasswordField(label=u"密碼", validators=[DataRequired(u"密碼不能爲空")]) user_ps_rt = PasswordField(label=u"確認密碼", validators=[DataRequired(u"確認密碼不能爲空"), EqualTo("user_ps", u"兩次密碼不一致")]) submit = SubmitField(label=u"提交") @app.route("/register", methods=["GET", "POST"]) def register(): # 建立表單對象,若是是post請求,前端發送了數據,flask會把數據在構造form對象的時候,存放到對象中 # 這樣的就不須要校驗請求方式是get仍是post form = RigsterForm() # 判斷form中的數據的合理性 # 若是form中數據徹底經過校驗則爲真 if form.validate_on_submit(): # 表示驗證合格 # 提取數據,必須提取到data,提取到字段名的時候,只是提取到一個對象而已 user_name = form.user_name.data pwd = form.user_ps.data pwd2 = form.user_ps_rt.data print(user_name) print(pwd) print(pwd2) session['user_name'] = user_name return redirect(url_for("index")) return render_template('register.html', form=form) @app.route("/index") def index(): name = session.get("user_name", "") return "hello %s" % name if __name__ == '__main__': app.run()
register.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <form method="post"> {{ form.csrf_token }} {{ form.user_name.label }} <p>{{ form.user_name }}</p> {% for msg in form.user_name.errors %} <p>{{ msg }}</p> {% endfor %} {{ form.user_ps.label }} <p>{{ form.user_ps }}</p> {% for msg in form.user_ps.errors %} <p>{{ msg }}</p> {% endfor %} {{ form.user_ps_rt.label }} <p>{{ form.user_ps_rt }}</p> {% for msg in form.user_ps_rt.errors %} <p>{{ msg }}</p> {% endfor %} {{ form.submit }} </form> </body> </html>