I have come to Flask from Django and in this post I am writing down my thoughts as I get more comfortable with Flask globals.
Request and Application Context
from flask import request, current_app, g
In that simple line, we have imported three global objects. One is the request object and other two are connected to the application object. These are also called proxy-objects because whenever you import them, you actually get an object that points to a global internal object that is maintained by Flask.
In Django, a Class Based View method takes in a request object as a parameter. In Flask, the pattern is to access this request object directly as a global. When it comes to application object, things are more at par between Django and Flask. In Django,
from django.conf import settings effectively does the same as
from flask import current_app, g.
@main.route("/") @main.route("/home") def site_home(): page = request.args.get("page", 1, type=int) posts = Post.query.order_by(Post.date_posted.desc()).paginate(page=page, per_page=5) return render_template("home.html", posts=posts)
The above code services the following url:
Here the request (pythonic-jargon is request context) is a global object maintained by flask. Each thread maintains its own request-context object. Context is in general a dict used in Django and Flask to pass context variables to templates. Here, we use the exact same context dict (a dict like object) to instead pass some globals packaged inside a global dict object called request-context.
Normally, request object is generated for you by the Flask core application. One scenario where we sometimes generate a request object ourselves is in tests:
Application Context: current_app
Adding to what we already talked about
g, the application context objects. The purpose of
current_app object is to keep environment specific information stored inside it and make it available globally. In other words, this object contains the Flask application object. So, using this object, you can get access to environment config related things like database connection string. Flask separated this information from regular request-context object because this information was not really related to request.
However, this is also a source of confusion. Even if application-context only stores application configuration related information and contains the pointer to flask application object, it is still not persisted between requests. Meaning, that for each new request, request-context is created and application-context is created and then torn down at the end of the request.
Application Context: g object
Quite confusingly named object, is also related to application context, exactly like current_app. g literally means global object. But this one is meant to be used to attach user specific information. Let’s switch to class based views in Flask. Here you have a similar hierarchy to Django. Now, in the dispatch_request method of the request, you can tack on the g object some value.. like
g['foo'] = 'bar'. You can then access this lower down in the request lifecycle, like get_context_data.
from flask import session
Session object (also called session cookie) is created inside a request object when a request object is created. We can access this object directly from the request object but working directly with this object is considered an anti-pattern. Instead we use the “session” object imported above. This gives a more user-friendly interface to managing session and gives an almost dict like interface to managing session information.
One key difference here is that this flask provided session-cookie is nearly the same as browser session cookie, the only difference being that this is cryptographically signed using the Flask Secret key. It has all the limits of the usual browser session cookie. If you want to actually have proper server-session, like in Django’s
django.contrib.sessions.middleware.SessionMiddleware, you will need extensions like Flask-Session and Flask-KVSession.
current_user is another global that is made available by flask-login extension. The info it exposes is already present in the request object but makes it easier.
Passing global context objects to background threads
Sometimes we need need to create background worker threads to do things like send email (proper way is to implement via Celery) but lets do it here using background threads.
Since the global objects are thread specific, the background thread will not have access to these. So, we pass these manually to the thread like so (flask jargon is, we need to push these contexts to the thread):
passing request context and application context to background thread
the magic lies in the decorator
See an example here https://www.kite.com/python/docs/flask.copy_current_request_context
To push the request context, the idea is that you decorate a function with this decorator. Then create a thread like so with the decorated function as target and the thread will have the request context pushed into it. For passing the application context, its bit manual. Note that args parameter in the Thread. In that parameter, you need to manually select which application context you wish to pass. And pass those as a tuple. I am passing the root_path of the current_app.
from flask from flask import current_app, copy_current_request_context @app.route('/home') def site_home(): @copy_current_request_context def send_mail_async(): pass Thread(target=send_mail_async, args=(current_app.root_path,)) return render_template("home.html")