本文介绍了Flask:为什么app.route()装饰器应该始终位于最外层?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

说,我有一个手工制作的 @login必需的装饰器:

Say, I have a hand-crafted @login-required decorator:

from functools import wraps

def login_required(decorated_function):
    """Decorator to check if user is logged in."""

        @wraps(decorated_function)
        def wrapper(*args, **kwargs):
        if False: # just to check it's working
            return decorated_function(*args, **kwargs)
        else:
            flash('You need to login, to access this page')
            return redirect(url_for('login'))
    return wrapper 

和一个用 @ app.route()和 @login_required (为简洁起见,省略了<$ c> login 的端点):

and a function, decorated with @app.route() and @login_required (endpoint for login omitted for brevity):

@app.route('/')
@login_required
def index():
    return "Hello!"

现在,如果我尝试访问 / ,正如预期的那样,它不会让我进入,而是将重定向到登录页面。


不过,如果我刷洗装饰器的顺序,即:

Now, if I try to access /, as expected, it won't let me and will redirect to the login page.
Though, if I swipe the the order of the decorators i.e.:

@login_required
@app.route('/')
def index():
    return "Hello!"

然后我就可以访问 /

我知道指出:

我也看到了 。

我很好奇的不是正确的方法( @ app.route()装饰器必须位于最外面-知道了),而是为什么它以这种方式工作(即,其背后的机制是什么)。

What I'm curious about is not what is the proper way to do it (@app.route() decorator must be outermost - got it), but rather why it is working this way (i.e. what is the mechanics behind it).

我看了 @ app.route()

   def route(self, rule, **options):
    def decorator(f):
        endpoint = options.pop('endpoint', None)
        self.add_url_rule(rule, endpoint, f, **options)
        return f
    return decorator

帮助我或多或少地了解了装饰器的机制。虽然,我之前从未见过函数刚刚返回(不调用它),所以我自己做了一个小实验(当然,这是可行的):

This answer, helped me to understand mechanism of decorators, more or less. Though, I have never seen function just returned (without calling it) before, so I did a little experiment myself (which turned out to be workable, of course):

def my_decorator():
    def decorator (function):
        return function
    return decorator

@my_decorator()
def test():
    print('Hi')

test()

所以,我想了解:


  • 为什么装饰器的顺序在上述确切情况下很重要,对于 @ app.route()和其他装饰器(我想这是相同的答案)?令我感到困惑的是, @ app.route()只是向应用程序添加了网址规则(即 self.add_url_rule(规则,终结点,f, ** options)并返回函数,就是这样,为什么订购很重要?

  • @ app.route()覆盖它上面的所有装饰器(如果这样的话)?

  • Why order of decorators matter in the exact case above and for @app.route() and other decorators in general (which is the same answer, I guess)? What confuses me, is that @app.route() just adds url rule to the app (i.e. self.add_url_rule(rule, endpoint, f, **options) and returns the function, that's it, so why would order matter?
  • Does @app.route() overrides all the decorators above it (how if so)?

我也是,装饰器应用程序的顺序是从下到上,尽管对我而言这并不能使事情变得更清楚。我想念什么? p>

I am also aware, that decorators application order is from bottom to top, though it doesn't make things any clearer, for me. What am I missing?

推荐答案

您几乎已经自己解释过了::-) app.route 确实

You have almost explained it yourself! :-) app.route does

self.add_url_rule(rule, endpoint, f, **options)

但是关键是 f 可以修饰任何功能。如果首先应用 app.route ,它将为原始功能(没有登录装饰器)添加一个URL规则。登录装饰器包装了该函数,但是 app.route 已经存储了原始的未包装版本,因此包装无效。

But the key is that f here is whatever function was decorated. If you apply app.route first, it adds a URL rule for the original function (without the login decorator). The login decorator wraps the function, but app.route has already stored the original unwrapped version, so the wrapping has no effect.

可能有助于设想展开装饰器。想象一下你是这样做的:

It may help to envision "unrolling" the decorators. Imagine you did it like this:

# plain function
def index():
    return "Hello!"

login_wrapped = login_required(index)         # login decorator
both_wrapped = app.route('/')(login_wrapped)  # route decorator

这是正确的方式,其中先进行登录换行,然后进行路由。在此版本中, app.route 所看到的功能已经用登录包装器包装了。错误的方式是:

This is the "right" way where the login wrap happens first and then the route. In this version, the function that app.route sees is already wrapped with the login wrapper. The wrong way is:

# plain function
def index():
    return "Hello!"

route_wrapped = app.route('/')(index)        # route decorator
both_wrapped = login_wrapped(route_wrapped)  # login decorator

在这里您可以看到 app.route 看到的只是普通的未包装版本。函数稍后用登录装饰器包装的事实无效,因为到那时路由装饰器已经完成。

Here you can see that what app.route sees is only the plain unwrapped version. The fact that the function is later wrapped with the login decorator has no effect, because by that time the route decorator has already finished.

这篇关于Flask:为什么app.route()装饰器应该始终位于最外层?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

10-13 13:16