|
|
|
@ -3,71 +3,43 @@
|
|
|
|
|
Deferred Request Callbacks |
|
|
|
|
========================== |
|
|
|
|
|
|
|
|
|
One of the design principles of Flask is that response objects are created |
|
|
|
|
and passed down a chain of potential callbacks that can modify them or |
|
|
|
|
replace them. When the request handling starts, there is no response |
|
|
|
|
object yet. It is created as necessary either by a view function or by |
|
|
|
|
some other component in the system. |
|
|
|
|
|
|
|
|
|
But what happens if you want to modify the response at a point where the |
|
|
|
|
response does not exist yet? A common example for that would be a |
|
|
|
|
before-request function that wants to set a cookie on the response object. |
|
|
|
|
|
|
|
|
|
One way is to avoid the situation. Very often that is possible. For |
|
|
|
|
instance you can try to move that logic into an after-request callback |
|
|
|
|
instead. Sometimes however moving that code there is just not a very |
|
|
|
|
pleasant experience or makes code look very awkward. |
|
|
|
|
|
|
|
|
|
As an alternative possibility you can attach a bunch of callback functions |
|
|
|
|
to the :data:`~flask.g` object and call them at the end of the request. |
|
|
|
|
This way you can defer code execution from anywhere in the application. |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
The Decorator |
|
|
|
|
------------- |
|
|
|
|
|
|
|
|
|
The following decorator is the key. It registers a function on a list on |
|
|
|
|
the :data:`~flask.g` object:: |
|
|
|
|
|
|
|
|
|
from flask import g |
|
|
|
|
|
|
|
|
|
def after_this_request(f): |
|
|
|
|
if not hasattr(g, 'after_request_callbacks'): |
|
|
|
|
g.after_request_callbacks = [] |
|
|
|
|
g.after_request_callbacks.append(f) |
|
|
|
|
return f |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Calling the Deferred |
|
|
|
|
-------------------- |
|
|
|
|
|
|
|
|
|
Now you can use the `after_this_request` decorator to mark a function to |
|
|
|
|
be called at the end of the request. But we still need to call them. For |
|
|
|
|
this the following function needs to be registered as |
|
|
|
|
:meth:`~flask.Flask.after_request` callback:: |
|
|
|
|
|
|
|
|
|
@app.after_request |
|
|
|
|
def call_after_request_callbacks(response): |
|
|
|
|
for callback in getattr(g, 'after_request_callbacks', ()): |
|
|
|
|
callback(response) |
|
|
|
|
return response |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
A Practical Example |
|
|
|
|
------------------- |
|
|
|
|
One of the design principles of Flask is that response objects are created and |
|
|
|
|
passed down a chain of potential callbacks that can modify them or replace |
|
|
|
|
them. When the request handling starts, there is no response object yet. It is |
|
|
|
|
created as necessary either by a view function or by some other component in |
|
|
|
|
the system. |
|
|
|
|
|
|
|
|
|
What happens if you want to modify the response at a point where the response |
|
|
|
|
does not exist yet? A common example for that would be a |
|
|
|
|
:meth:`~flask.Flask.before_request` callback that wants to set a cookie on the |
|
|
|
|
response object. |
|
|
|
|
|
|
|
|
|
One way is to avoid the situation. Very often that is possible. For instance |
|
|
|
|
you can try to move that logic into a :meth:`~flask.Flask.after_request` |
|
|
|
|
callback instead. However, sometimes moving code there makes it more |
|
|
|
|
more complicated or awkward to reason about. |
|
|
|
|
|
|
|
|
|
As an alternative, you can use :func:`~flask.after_this_request` to register |
|
|
|
|
callbacks that will execute after only the current request. This way you can |
|
|
|
|
defer code execution from anywhere in the application, based on the current |
|
|
|
|
request. |
|
|
|
|
|
|
|
|
|
At any time during a request, we can register a function to be called at the |
|
|
|
|
end of the request. For example you can remember the current language of the |
|
|
|
|
user in a cookie in the before-request function:: |
|
|
|
|
end of the request. For example you can remember the current language of the |
|
|
|
|
user in a cookie in a :meth:`~flask.Flask.before_request` callback:: |
|
|
|
|
|
|
|
|
|
from flask import request, after_this_request |
|
|
|
|
|
|
|
|
|
@app.before_request |
|
|
|
|
def detect_user_language(): |
|
|
|
|
language = request.cookies.get('user_lang') |
|
|
|
|
|
|
|
|
|
if language is None: |
|
|
|
|
language = guess_language_from_request() |
|
|
|
|
|
|
|
|
|
# when the response exists, set a cookie with the language |
|
|
|
|
@after_this_request |
|
|
|
|
def remember_language(response): |
|
|
|
|
response.set_cookie('user_lang', language) |
|
|
|
|
|
|
|
|
|
g.language = language |
|
|
|
|