Flask(10)- 標準類檢視

小菠蘿測試筆記發表於2021-07-14

前言

  • 前面文章講解 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:  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()
  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:  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] 設定檢視類的裝飾器

 

相關文章