前言
- 前面文章講解 Flask 路由的時候,都是將 URL 路徑和一個檢視函式關聯
- 當 Flask 框架接收到請求後,會根據請求 URL,呼叫響應的檢視函式進行處理
- Flask 不僅提供了檢視函式來處理請求,還提供了檢視類;可以將 URL 路徑和一個檢視類關聯
標準檢視函式
- 將 URL 路徑和一個函式關聯,這個函式又被稱為檢視函式,Flask 框架會根據請求的 URL 呼叫相應的檢視函式進行處理
- 當訪問 127.0.0.1:5000/ 時,index() 函式就會處理該請求,並返回 hello world 字串
from flask import Flask app = Flask(__name__) @app.route('/') def index(): return 'hello world' app.run(debug = True)
標準檢視類
Flask.views.View 是 Flask 的標準檢視類,使用者定義的檢視類需要繼承於 Flask.views.View 。使用檢視類的步驟如下:
- 使用者定義一個檢視類,繼承於 Flask.views.View;
- 在檢視類中定義方法 dispatch_request ,處理請求、返回 HTML 文字給客戶端;
- 使用 app.add_url_rule (rule, view_func) 將 URL 路徑和檢視類繫結
最簡單的栗子
#!usr/bin/env python # -*- coding:utf-8 _*- """ # author: 小菠蘿測試筆記 # blog: https://www.cnblogs.com/poloyy/ # time: 2021/7/13 8:42 下午 # file: 8_view_class.py """ from flask import Flask, views from flask.typing import ResponseReturnValue app = Flask(__name__) # 自定義檢視類,繼承 views.View class view_test(views.View): # 返回一個字串給客戶端 def dispatch_request(self) -> ResponseReturnValue: return "hello world" # 將路由規則 / 和檢視類 view_test 進行繫結 app.add_url_rule(rule="/", view_func=view_test.as_view("view")) if __name__ == '__main__': app.run()
重點 as_view
- view_test.as_view("view") 代表建立了一個名稱為 view 的檢視函式
- app.add_url_rule 實際上是將路由規則和檢視函式(由檢視類的 as_view 方法轉換而來)繫結
瀏覽器訪問的效果
as_view 函式
檢視類的本質是檢視函式,函式 View.as_view () 會返回一個檢視函式
簡化版
為了更清晰理解 as_view 函式的功能,自行實現一個簡化版本的 as_view 函式
#!usr/bin/env python # -*- coding:utf-8 _*- """ # author: 小菠蘿測試筆記 # blog: https://www.cnblogs.com/poloyy/ # time: 2021/7/13 8:42 下午 # file: 8_view_class.py """ from flask import Flask, views from flask.typing import ResponseReturnValue app = Flask(__name__) class view_test2(views.View): def dispatch_request(self) -> ResponseReturnValue: return {"msg": "success", "code": 0} @staticmethod def as_view(name, **kwargs): view = view_test2() return view.dispatch_request # 將路由規則 / 和檢視類 view_test 進行繫結 app.add_url_rule(rule="/", view_func=view_test2.as_view("view")) if __name__ == '__main__': app.run()
- 定義了一個靜態方法 as_view,它首先建立一個例項 view
- 然後返回例項 view 的 dispatch_request 方法
- 即 view_func 指向了例項 view 的方法 dispatch_request
- 當訪問頁面路徑 / 時,最終會呼叫 index.dispatch_request ()
繼承
使用類檢視的好處是支援繼承,可以把一些共性的東西放在父類中,其他子類可以繼承
父類 baseview
#!usr/bin/env python # -*- coding:utf-8 _*- """ # author: 小菠蘿測試筆記 # blog: https://www.cnblogs.com/poloyy/ # time: 2021/7/13 10:15 下午 # file: s8_baseview.py """ from flask import Flask, views, render_template app = Flask(__name__) class BaseView(views.View): # 如果子類忘記定義 get_template 就會報錯 def get_template(self): raise NotImplementedError() # 如果子類忘記定義 get_data 就會報錯 def get_data(self): raise NotImplementedError() def dispatch_request(self): # 獲取模板需要的資料 data = self.get_data() # 獲取模板檔案路徑 template = self.get_template() # 渲染模板檔案 return render_template(template, **data)
子類 userview
#!usr/bin/env python # -*- coding:utf-8 _*- """ # author: 小菠蘿測試筆記 # blog: https://www.cnblogs.com/poloyy/ # time: 2021/7/13 10:15 下午 # file: 8_userview.py """ from s8_baseview import BaseView class UserView(BaseView): def get_template(self): return "user.html" def get_data(self): return { 'name': 'zhangsan', 'gender': 'male', }
app.py 應用主入口
#!usr/bin/env python # -*- coding:utf-8 _*- """ # author: 小菠蘿測試筆記 # blog: https://www.cnblogs.com/poloyy/ # time: 2021/7/13 10:31 下午 # file: 8_app.py """ from flask import Flask, views from s8_userview import UserView app = Flask(__name__) app.add_url_rule('/user/', view_func=UserView.as_view('UserView')) app.run(debug=True)
user.html 程式碼
<html> <body> <h2>name = {{ name }}</h2> <h2>gender = {{ gender }}</h2> </body> </html>
瀏覽器訪問的效果
使用裝飾器
在檢視函式、檢視類中使用裝飾器還是一大殺器
檢查登入功能
不使用裝飾器前的程式碼
def check_login(): if 使用者已經登入: return True else: return False @app.route('/page1', page1) def page1(): if not check_login(): return '請先登入' 執行 page1 的功能 @app.route('/page2', page2) def page2(): if not check_login(): return '請先登入' 執行 page2 的功能
- 在處理 /page1 和 /page2 時需要檢查登入,在函式 page1 () 和 page2 () 的頭部呼叫 check_login 函式
- 這種方法雖然實現了功能,但不夠簡潔
檢查登入的裝飾器
使用裝飾器實現登入的功能,定義檢查登入的裝飾器 check_login
from flask import request from functools import wraps def check_login(original_function): @wraps(original_function) def decorated_function(*args, **kwargs): user = request.args.get("user") if user and user == "zhangsan": return original_function(*args, **kwargs) else: return "請登入" return decorated_function()
- 裝飾器 check_login 本質是一個函式
- 它的輸入是一個函式 original_function
- 它的輸出也是一個函式 decorated_function
- original_function 是原先的處理 URL 的檢視函式,它不包含檢查登入的功能邏輯,就是到時候需要新增裝飾器的函式
- decorated_function 是在 original_function 的基礎上進行功能擴充的函式(這就是裝飾器的功能),它首先檢查是否已經登入,如果已經登入則呼叫 original_function,如果沒有登入則返回錯誤
- 使用 functools.wraps (original_function) 保留原始函式 original_function 的屬性
在檢視函式中使用裝飾器
#!usr/bin/env python # -*- coding:utf-8 _*- """ # author: 小菠蘿測試筆記 # blog: https://www.cnblogs.com/poloyy/ # time: 2021/7/13 10:31 下午 # file: 8_app.py """ from flask import Flask, request from functools import wraps app = Flask(__name__) # 定義裝飾器 def check_login(original_function): @wraps(original_function) def decorated_function(*args, **kwargs): user = request.args.get("user") if user and user == "zhangsan": return original_function(*args, **kwargs) else: return "請登入" return decorated_function() @app.route("/page1") @check_login def page1(): return "page1" @app.route("/page2") @check_login def page2(): return "page2" app.run(debug=True)
- page1、page2 兩個檢視函式更關注請求處理,而檢查登入的功能交給裝飾器去負責
- 這樣,檢查登入的功能與 page1 和 page2 本身的功能是分離的
瀏覽器訪問的效果
在檢視類中使用裝飾器
#!usr/bin/env python # -*- coding:utf-8 _*- """ # author: 小菠蘿測試筆記 # blog: https://www.cnblogs.com/poloyy/ # time: 2021/7/13 11:06 下午 # file: 8_viewclass_decorated.py """ from flask import Flask, request, views from functools import wraps app = Flask(__name__) def check_login(original_function): @wraps(original_function) def decorated_function(*args, **kwargs): user = request.args.get("user") if user and user == 'zhangsan': return original_function(*args, **kwargs) else: return '請先登入' return decorated_function class Page1(views.View): decorators = [check_login] def dispatch_request(self): return 'Page1' class Page2(views.View): decorators = [check_login] def dispatch_request(self): return 'Page2' app.add_url_rule(rule='/page1', view_func=Page1.as_view('Page1')) app.add_url_rule(rule='/page2', view_func=Page2.as_view('Page2')) app.run(debug=True)
decorators = [check_login] 設定檢視類的裝飾器