When does Flask's route decorator execute? - python

When is decorator in Flask's route method executed? Specifically, I want to know when self.add_url_rule() is executed.
from flask import Flask
app = Flask(__name__)
#app.route("/")
def root_of_app():
load_root_of_app()
Is add_url_rule executed when the module containing root_of_app is first imported, or when root_of_app is first called by a web request?
Here is the source for the route function:
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

You can verify this yourself by adding print statements to the route decorator.
When route is called, it builds a decorator. That decorator is then applied to the view by calling it. Both these happen at import, because importing executes module-level code.
Using #app.route() registers the view, it is not deferred until the first request. The blueprint version of route is deferred until the blueprint is registered on the app, which also happens before the first request.

Related

Access flask.g inside greenlet

I'm using Flask + gevent and want to access the flask.g application global inside the target function of a greenlet.
I'm using the copy_current_request_context decorator and have a situation pretty similar to example given in the docs:
import gevent
from flask import copy_current_request_context, g
#app.route('/')
def index():
g.user_data = 'foobar'
g.more_user_data = 'baz'
#copy_current_request_context
def do_some_work():
some_func(g.user_data, g.more_user_data)
...
gevent.spawn(do_some_work)
return 'Regular response'
However, I get the following error:
AttributeError: '_AppCtxGlobals' object has no attribute 'user_data'
I think a new application context is pushed when the request context is copied? I set a trace in the Flask code here and that seems to be the case. So the error isn't all that surprising because the flask.g object is application context scoped as of 0.10 (see http://flask.pocoo.org/docs/0.12/api/#flask.Flask.app_ctx_globals_class).
Obviously, I can just pass the user data into the target function as arguments:
import gevent
from flask import g
#app.route('/')
def index():
g.user_data = 'foobar'
g.more_user_data = 'baz'
def do_some_work(user_data, more_user_data):
some_func(user_data, more_user_data)
...
gevent.spawn(do_some_work, g.user_data, g.more_user_data)
return 'Regular response'
And this works just fine, but I was hoping to use flask.g if possible.
flask.g is bound with the app context, not on request context, as the doc says:
Starting with Flask 0.10 this is stored on the application context and no longer on the request context ...
copy_current_request_context() only copy request context, but give you a new app context. You could create one to pass current app context with closure:
def copy_current_app_context(f):
from flask.globals import _app_ctx_stack
appctx = _app_ctx_stack.top
def _(*args, **kwargs):
with appctx:
return f(*args, **kwargs)
return _
However, I prefer pass data to greenlet explicitly via arguments, which is cleaner.

Flask: why app.route() decorator, should always be the outermost?

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?
You have almost explained it yourself! :-) app.route does
self.add_url_rule(rule, endpoint, f, **options)
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
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
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: How to run a method before every route in a blueprint?

I want to make my Flask Blueprint always run a method before executing any routes. Instead of decorating every route method in my blueprint with a custom decorator, I want to be able to do something like this:
def my_method():
do_stuff
section = Blueprint('section', __name__)
# Register my_method() as a setup method that runs before all routes
section.custom_setup_method(my_method())
#section.route('/two')
def route_one():
do_stuff
#section.route('/one')
def route_two():
do_stuff
Then basically both /section/one and /section/two will run my_method() before executing code in route_one() or route_two().
Is there a way to do this?
You can use the before_request decorator for blueprints. Like this:
#section.before_request
def my_method():
do_stuff
This automatically registers the function to run before any routes that belong to the blueprint.
You can use before_request for this:
#section.before_request
def before_request():
do_stuff
http://flask.pocoo.org/docs/0.10/api/#flask.Flask.before_request

working outside of aplication context in flask with a self made decorator

I'm doing a blog for my self from scratch and everything is working, even the sessions.
Now I'm trying to limit the admin with a decorator called #require_login. I think I'm doing something really wrong, besides the fact that is not working.
Here is my decorator:
def requirir_login(func):
request = make_response()
if session['logged_in'] == True:
return func
else:
print("no hay sesion registrada webon")
and here is it used, decorating the admin function:
#app.route("/admin")
#requirir_login
def admin():
users = User.objects
return render_template("admin.html", users=users)
My logic behind this is to check if there is a session then return the admin function. If not I wanted to check in the terminal that message for test purposes.
I haven't decided what to do if there is not a session yet. I would possibly redirect to the log-in page or something.
Your decorator needs to provide a wrapper function, which will be called in place of the decorated function. Only when that wrapper is being called is an actual request being routed and can you test the session:
from functools import wraps
def requirir_login(func):
#wraps(func)
def wrapper(*args, **kwargs):
if session['logged_in']:
return func(*args, **kwargs)
else:
print("no hay sesion registrada webon")
return wrapper
When a decorator is applied, it is called an its return value replaces the decorated function. Here wrapper is returned, so that now becomes your view function.
The wrapper passes on all arguments untouched, making your decorator suitable for any view function regardless of the arguments they expect to be passed in from the route.
I also made a few other changes to improve the functionality of your decorator:
You don't need to test for == True; that is what if is for, to test if the result of an expression is true or not.
I used the #functools.wraps() decorator to give your wrapper the same name and documentation string as the original wrapped view function, always helpful when debugging.
You could indeed use a redirect to the login form if you have one:
return redirect(url_for('login'))
if your login view is named login.

What is being inherited by class flask.views.MethodView?

Why are we passing flask.views.MethodView in the class?
app.add_url_rule > in this snippet, add_url_rule is this something predefined property?
Similarly view_func, View.as_view > are they predefined?
import flask, flask.views
app = flask.Flask(__name__)
class View(flask.views.MethodView):
def get(self):
return "Hello World!"
app.add_url_rule('/',view_func=View.as_view('main'))
app.debug = True
app.run()
View is a subclass of the flask.views.MethodView class. The latter provides base functionality, like the as_view() method:
Converts the class into an actual view function that can be used with the routing system. Internally this generates a function on the fly which will instantiate the View on each request and call the dispatch_request() method on it.
Also see Pluggable Views.
Because this is not a function-based view, you cannot use the #app.route() decorator on it. You use the alternative app.add_url_rule() method instead in that case:
Connects a URL rule. Works exactly like the route() decorator. If a view_func is provided it will be registered with the endpoint.
view_func is a documented keyword argument for this method; when the registered path is requested (in your example /), then whatever as_view() returned is called by Flask. That in turn calls dispatch_request(), which then calls the View().get() method if the HTTP request used a GET method.

Categories

Resources