一、前言
參考https://www.cnblogs.com/poloyy/p/15008909.html,http://www.imooc.com/wiki/flasklesson/flaskview.html
- 前面文章講解 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 路徑和視圖類綁定
下面是視圖類的例子app.py
from flask import Flask, views app = Flask(__name__) class Index(views.View) : def dispatch_request(self): return 'hello world' app.add_url_rule(rule='/', view_func = Index.as_view('Index')) app.run(debug = True)
在第 4 行,定義視圖類 Index 用於處理路徑爲 / 的 URL,視圖類 Index 繼承於 Flask.views.View;在第 5 行,定義方法 dispatch_request,該方法返回 ‘hello world’ 給客戶端;在第 8 行,將路徑爲 / 的 URL 和視圖類 Index 綁定。
Tips:Index.as_view (‘Index’) 創建一個名稱爲 Index 的視圖函數,app.add_url_rule 實際上是將 URL 路徑和視圖函數(由視圖類的 as_view 轉換而來)綁定。
函數 View.as_view () 返回一個視圖函數
四、繼承
使用類視圖的好處是支持繼承,可以把一些共性的東西放在父類中,其他子類可以繼承。下面通過一個例子 inherit.py 說明如何使用繼承。
4.1 父類 BaseView
首先,定義視圖類 BaseView,它繼承於 flask.views.View,它是用戶自定義的視圖類的父類,app.py代碼如下:
from flask import Flask, views, render_template app = Flask(__name__) class BaseView(views.View): def get_template(self): raise NotImplementedError() def get_data(self): raise NotImplementedError() def dispatch_request(self): data = self.get_data() template = self.get_template() return render_template(template,**data)
BaseView 與子類之間的關係如下:BaseView 提供了 dispatch_request 方法的實現,子類繼承了這個方法,不需要重新實現 dispatch_request;BaseView 定義了兩個接口函數 get_template 和 get_data,子類必須實現這兩個方法。
在 BaseView 中,get_template 和 get_data 的缺省實現是拋出錯誤 NotImplementedError,如果子類忘記定義了這兩個方法,在運行時會報錯。
在 BaseView 中,定義了方法 displach_request (),調用 get_template () 獲得模板的路徑,調用 get_data () 獲取模板的參數,最後調用 render_template 根據模板路徑 template 和模板參數 data 渲染輸出。
4.2 子類 UserView
實現子類 UserView,它繼承於 BaseView,user.py代碼如下:
from app import BaseView from flask import Flask app = Flask(__name__) class UserView(BaseView): def get_template(self): return "index.html" def get_data(self): return { 'name': "zhangsan", 'age':13 } app.add_url_rule('/user/',view_func=UserView.as_view('UserView')) if __name__ == '__main__': app.run(debug=True)
在子類 UserView 中,get_template 返回模板路徑爲 ‘user.html’,get_data 返回模板參數 name 和 gender。BaseView 中已經實現了視圖類的 dispatch_request 方法,子類 UserView 繼承了 BaseView 的 dispatch_request 方法,因此不需要再重新實現該方法。
將路徑爲 /user/ 的 URL 和視圖類 UserView 綁定,當訪問路徑爲 /user/ 的 URL 時,最終由 BaseView.dispatch_request 進行處理。
4.3 模板 user.html
在templates目錄下創建模板 index.html:
<html> <body> <h2>name = {{ name }}</h2> <h2>age = {{ age }}</h2> </body> </html>
4.4 運行結果
在瀏覽器中訪問http://127.0.0.1:5000/user/,顯示如下:
五、使用裝飾器
5.1 檢查登錄的裝飾器
使用裝飾器實現登錄的功能,定義檢查登錄的裝飾器 check_login
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,如果沒有登錄則返回錯誤。
在第 6 行和第 7 行,檢查請求中的參數 user 是否爲 ‘zhangsan’,如果 user 等於 ‘zhangsan’,表示用戶已經登錄,則調用 original_function,否則返回 ‘請先登錄’。
在第 4 行,使用 functools.wraps (original_function) 保留原始函數 original_function 的屬性。
5.2 在視圖函數中使用裝飾器
創建文件 app.py:
from functools import wraps from flask import request, Flask 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' if __name__ == '__main__': app.run(debug=True)
程序有 2 個頁面 /page1 和 /page2,只有登錄的用戶才能訪問這兩個頁面。
函數 page1 被裝飾了 2 次,它的原始功能是處理 /page1 的頁面邏輯,被 @check_login 裝飾後,具備了檢查登錄的功能,被 @app.route (’/page1’) 裝飾後,綁定到路徑爲 /page1 的 URL,當訪問 /page1 時,會訪問 page1 函數。函數 page2 的功能類似。
登錄後訪問的頁面:登錄後即指url帶user參數
5.3 在視圖類中使用裝飾器
創建文件 app.py:
from functools import wraps from flask import request, Flask,views 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=Page1.as_view('Page2')) if __name__ == '__main__': app.run(debug=True)
程序有 2 個頁面 /page1 和 /page2,只有登錄的用戶才能訪問這兩個頁面。
類 Page1 的原始功能是處理 /page1 的頁面邏輯,在第 9 行,decorators = [check_login] 設定視圖類的裝飾器,當訪問 /page1 時,首先執行檢查登錄,然後再執行原始的功能。
運行效果跟視圖函數一致。