BUG 小記:Flask使用自定義裝飾器

問題描述

Flask Web 項目,遇到一個需求:

部分視圖,只有已經登錄的用戶才能訪問,如果用戶訪問時沒有登錄,則會重定向到登錄頁面。

手動爲每個視圖函數添加判讀語句太過繁瑣,遂決定使用裝飾器實現這個功能,代碼大概如下所示:

from flask import Flask,request,redirect

app = Flask(__name__)

def check_login_status(func):
    def wrapper(*args, **kwargs):
        if request.cookies:
            return func(*args, **kwargs)
        else:
            # 否則重定向到登錄頁面
            return redirect("/login/")
    return wrapper

@app.route("/",endpoint="index")
@check_login_status
def index():
    return "首頁"

print(index.__name__)

@app.route("/news/",endpoint="news")
@check_login_status
def news():
    return "新聞界面"

但是執行時,卻發生瞭如下錯誤:

AssertionError: View function mapping is overwriting an existing endpoint function: wrapper

問題思考

這是一個新手經常會遇到的錯誤,如果不小心把視圖函數寫重名,就會出現這個錯誤,如下所示:

img

但是這裏視圖函數名並沒有問題,爲什麼也出現了這個問題?

一頓查閱資料後得知,是 裝飾器 出現了問題!

經過 裝飾器 裝飾之後的函數,它們的 __name__ 已經從原來的函數名變成 wrapper,也就是變成了裝飾器內部的函數名稱

我們可以通過打印函數的 __name__ 看到這一結果:

>>> @app.route("/")
>>> @check_login_status
>>> def index():
...    return "首頁"

>>> print(index.__name__)

wrapper

解決方案

知道了出錯的原因,我們就可以對症下藥了,具體的解決辦法有如下幾種:

(1) 把每個 wrapper.__name__ 設置成唯一的

def check_login_status(func):
    def wrapper(*args, **kwargs):
        if request.cookies:
            return func(*args, **kwargs)
        else:
            # 否則重定向到登錄頁面
            return redirect("/login/")
    wrapper.__name__ = func.__name__
    return wrapper

(2) 使用 functools.wraps 裝飾下 wrapper 函數

其實這個辦法做的事情和上一個辦法是差不多的,只不過除了 __name__ 之外,它還把 __module____doc____dict__ 都複製到 wrapper 上去了

from functools import wraps
def check_login_status(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        # 如果獲取到 session , 即判定爲 已登錄
        if request.cookies:
            return func(*args, **kwargs)
        else:
            # 否則重定向到登錄頁面
            return redirect("/login/")
    return wrapper

(3) 顯式設置每個視圖函數的 endpoint 名稱

@app.route("/",endpoint="index")
@check_login_status
def index():
    return "首頁"

@app.route("/news/",endpoint="news")
@check_login_status
def news():
    return "新聞界面"

參考文檔

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章