Flask(10)- 標準類視圖

前言

  • 前面文章講解 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 。使用視圖類的步驟如下:

  1. 用戶定義一個視圖類,繼承於 Flask.views.View;
  2. 在視圖類中定義方法 dispatch_request ,處理請求、返回 HTML 文本給客戶端;
  3. 使用 app.add_url_rule (rule, view_func) 將 URL 路徑和視圖類綁定

 

最簡單的栗子

#!usr/bin/env python
# -*- coding:utf-8 _*-
"""
# author: 小菠蘿測試筆記
# blog:  //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:  //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()
  1. 定義了一個靜態方法 as_view,它首先創建一個實例 view
  2. 然後返回實例 view 的 dispatch_request 方法
  3. 即 view_func 指向了實例 view 的方法 dispatch_request
  4. 當訪問頁面路徑 / 時,最終會調用 index.dispatch_request ()

 

繼承

使用類視圖的好處是支援繼承,可以把一些共性的東西放在父類中,其他子類可以繼承

 

父類 baseview

#!usr/bin/env python
# -*- coding:utf-8 _*-
"""
# author: 小菠蘿測試筆記
# blog:  //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:  //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:  //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:  //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:  //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] 設定視圖類的裝飾器