【问题标题】:Why does Flask's url_for throw an error when using a decorator on that item in Python?为什么在 Python 中对该项目使用装饰器时,Flask 的 url_for 会引发错误?
【发布时间】:2012-12-16 08:41:57
【问题描述】:

我正在创建一个 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)

【问题讨论】:

    标签: python flask decorator build-error


    【解决方案1】:

    这里的问题是您返回的 decorator() 函数与它正在装饰的函数的名称不同,因此 URL 构建器无法找到您的 index 视图。您需要使用来自functools 模块的wraps() 装饰器来复制原始函数的名称。另一个问题(您仍然必须遇到)是您不接受装饰器中的参数并将其传递给原始函数。这是修正后的装饰器:

    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 中,装饰器是一个函数,它接受另一个函数作为其参数并返回一个函数作为其结果。所以以下

    @logged_in
    def index(): pass
    

    本质上与

    相同
    def index(): pass
    index = logged_in(index)
    

    在这种情况下的问题是您的logged_in 装饰器返回的不是原始函数,而是一个包装器(在您的代码中称为decorator),它包装了原始函数。此包装器的名称 (decorator) 与其包装的原始函数不同。现在app.route() 装饰器,你在logged_in 之后调用,看到这个新函数并使用它的名字(decorator)为它注册一个路由。问题出在这里:您希望装饰函数具有相同的名称 (index),因此可以在 url_for() 中使用它来获取它的路由。这就是为什么您需要手动复制名称

    decorator.__name__ = fn.__name__
    

    或者更好地使用来自functools 模块的update_wrapperwraps 助手,它们可以为您做到这一点,甚至更多。

    【讨论】:

    • 你能解释一下“不同的签名”是什么意思吗?
    • 感谢您的提问,您让我意识到我自己错误地使用了这个词。我真正的意思是“不同的函数名”。我将更新答案以使用正确的术语并添加更多解释。如果您想了解更多关于装饰器和“调用签名”问题的信息,请前往 decorator 模块中出色的 documentation,因为我觉得这超出了这个问题的范围。
    猜你喜欢
    • 2012-09-04
    • 2013-06-30
    • 1970-01-01
    • 2018-02-03
    • 1970-01-01
    • 2020-07-24
    • 1970-01-01
    • 2018-05-08
    • 1970-01-01
    相关资源
    最近更新 更多