問題描述
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
問題思考
這是一個新手經常會遇到的錯誤,如果不小心把視圖函數寫重名,就會出現這個錯誤,如下所示:
但是這裏視圖函數名並沒有問題,爲什麼也出現了這個問題?
一頓查閱資料後得知,是 裝飾器 出現了問題!
經過 裝飾器 裝飾之後的函數,它們的 __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 "新聞界面"