問題描述
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 "新聞界面"
參考文檔
- 視圖裝飾器 — Flask 中文文檔
- 實作Flask中view函數的裝飾器
- 裝飾器 - 廖雪峰的官方網站