我正在创建一个Python Flask应用程序,并创建了下面的装饰器和视图。在查看索引时,装饰器工作得很好,但是当您注销并使用url_for
索引重定向时,它会抛出构建器错误。为什么
def logged_in(fn):
def decorator():
if 'email' in session:
return fn()
else:
return render_template('not-allowed.html', page="index")
return decorator
@app.route('/')
@logged_in
def index():
email = session['email']
return render_template('index.html', auth=True, page="index", marks=marks)
@app.route('/sign-out')
def sign_out():
session.pop('email')
print(url_for('index'))
return redirect(url_for('index'))
任何想法?错误是:BuildError: ('index', {}, None)
这里的问题是您返回的decorator()
函数的名称不同于它正在装饰的函数,因此URL构建器无法找到您的index
视图。您需要使用functools
模块中的wraps()
装饰器来复制原始函数的名称。另一个问题(您仍然必须遇到)是,您不接受decorator中的参数并将其传递给原始函数。下面是修改后的装饰符:
from functools import wraps
def logged_in(fn):
@wraps(fn)
def decorator(*args, **kwargs):
if 'email' in session:
return fn(*args, **kwargs)
else:
# IMO it's nicer to abort here and handle it in errorhandler.
abort(401)
return decorator
再解释一下:在Python中,decorator是一个函数,它接受另一个函数作为参数并返回一个函数作为结果。所以下面的
@logged_in
def index(): pass
本质上等同于
def index(): pass
index = logged_in(index)
在这种情况下的问题是,您的logged_in
装饰器返回的不是原始函数,而是包装器(在您的代码中称为decorator
),它包装了原始函数。这个包装器的名称(decorator
)与它所包装的原始函数不同。现在,在logged_in
之后调用的app.route()
装饰器看到了这个新函数,并使用它的名称(decorator
)为它注册了一个路由。这里有一个问题:您希望装饰函数具有相同的名称(index
),因此可以在url_for()
中使用它来获取它的路由。这就是为什么您需要手动复制名称
decorator.__name__ = fn.__name__
或者更好地使用functools
模块中的update_wrapper
和wraps
帮助程序,它们可以为您完成这些工作,甚至更多。