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
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!"
then I am able to access /
, even though I shouldn't be.
I am aware that Flask documentation on the subject states:
When applying further decorators, always remember that the route() decorator is the outermost.
I have also seen other questions on the same issue.
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).
I took a look at @app.route()
source code:
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()
So, I would like to understand:
- 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)?
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?