1 python decorator flask python-decorators
说,我有一个手工制作的@login-required装饰器:
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
Run Code Online (Sandbox Code Playgroud)
和一个用@app.route()and装饰的函数@login_required(为简洁起见省略了端点login):
@app.route('/')
@login_required
def index():
return "Hello!"
Run Code Online (Sandbox Code Playgroud)
现在,如果我/按预期尝试访问,它不会让我访问并将重定向到登录页面。
但是,如果我滑动装饰器的顺序,即:
@login_required
@app.route('/')
def index():
return "Hello!"
Run Code Online (Sandbox Code Playgroud)
然后我就可以访问了/,即使我不应该访问。
我知道有关该主题的 Flask 文档指出:
在应用更多装饰器时,请始终记住 route() 装饰器是最外层的。
我好奇的不是什么是正确的方法(@app.route()装饰器必须是最外面的 - 明白了),而是为什么它以这种方式工作(即它背后的机制是什么)。
我看了一下@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
Run Code Online (Sandbox Code Playgroud)
这个答案,或多或少地帮助我了解了装饰器的机制。不过,我之前从未见过函数刚刚返回(没有调用它),所以我自己做了一个小实验(结果当然是可行的):
def my_decorator():
def decorator (function):
return function
return decorator
@my_decorator()
def test():
print('Hi')
test()
Run Code Online (Sandbox Code Playgroud)
所以,我想明白:
@app.route()一般来说对于和其他装饰器(我猜这是相同的答案)?让我困惑的是,这@app.route()只是向应用程序添加了 url 规则(即self.add_url_rule(rule, endpoint, f, **options)并返回函数,就是这样,那么为什么顺序很重要呢?@app.route()覆盖了它上面的所有装饰器(如果是的话如何)?我也知道,装饰器应用程序的顺序是从下到上的,尽管对我来说它并没有让事情变得更清楚。我错过了什么?
你几乎自己解释过了!:-) app.route确实
self.add_url_rule(rule, endpoint, f, **options)
Run Code Online (Sandbox Code Playgroud)
但关键是f这里是装饰的任何函数。如果您app.route先申请,它会为原始函数添加一个 URL 规则(没有登录装饰器)。登录装饰器对函数进行了包装,但app.route已经存储了原始未包装的版本,因此包装没有任何作用。
设想“展开”装饰器可能会有所帮助。想象一下你是这样做的:
# plain function
def index():
return "Hello!"
login_wrapped = login_required(index) # login decorator
both_wrapped = app.route('/')(login_wrapped) # route decorator
Run Code Online (Sandbox Code Playgroud)
这是登录包装首先发生然后路由发生的“正确”方式。在这个版本中,app.route看到的函数已经用登录包装器包装了。错误的方法是:
# plain function
def index():
return "Hello!"
route_wrapped = app.route('/')(index) # route decorator
both_wrapped = login_wrapped(route_wrapped) # login decorator
Run Code Online (Sandbox Code Playgroud)
在这里你可以看到app.route看到的只是普通的解包版本。该函数后来用 login 装饰器包装的事实没有任何影响,因为到那时路由装饰器已经完成。
| 归档时间: |
|
| 查看次数: |
5212 次 |
| 最近记录: |