当在Python中对该项目使用装饰器时,为什么Flask的url_for会引发错误?

发布于 2021-01-29 14:10:36

我正在创建一个Python
Flask应用程序,并在下面创建了装饰器和视图。装饰器在查看索引时效果很好,但是当您注销并使用url_for索引重定向时,它将引发builderror。为什么会

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)

关注者
0
被浏览
149
1 个回答
  • 面试哥
    面试哥 2021-01-29
    为面试而生,有面试问题,就找面试哥。

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

    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(),您将在之后调用的decoratorlogged_in看到此新函数,并使用其名称(decorator)为其注册路由。问题就出在这里:您希望修饰后的函数具有相同的名称(index),因此可用于url_for()为其获取路线。这就是为什么您需要手动复制名称的原因

    decorator.__name__ = fn.__name__
    

    或更好地使用模块中的帮助update_wrapperwraps帮助functools,为您做的甚至更多。



知识点
面圈网VIP题库

面圈网VIP题库全新上线,海量真题题库资源。 90大类考试,超10万份考试真题开放下载啦

去下载看看