diff --git a/docs/patterns/deferredcallbacks.rst b/docs/patterns/deferredcallbacks.rst new file mode 100644 index 00000000..917c5125 --- /dev/null +++ b/docs/patterns/deferredcallbacks.rst @@ -0,0 +1,73 @@ +.. _deferred-callbacks: + +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 then 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', ()): + response = callback(response) + return response + + +A Practical Example +------------------- + +Now we can easily at any point in time register a function to be called at +the end of this particular request. For example you can remember the +current language of the user in a cookie in the before-request function:: + + from flask import request + + @app.before_request + def detect_user_language(): + language = request.cookies.get('user_lang') + if language is None: + language = guess_language_from_request() + @after_this_request + def remember_language(response): + response.set_cookie('user_lang', language) + g.language = language diff --git a/docs/patterns/index.rst b/docs/patterns/index.rst index 3a1e409c..6f63df11 100644 --- a/docs/patterns/index.rst +++ b/docs/patterns/index.rst @@ -36,3 +36,4 @@ Snippet Archives `_. mongokit favicon streaming + cookies diff --git a/docs/quickstart.rst b/docs/quickstart.rst index 690433db..34aa3be4 100644 --- a/docs/quickstart.rst +++ b/docs/quickstart.rst @@ -621,6 +621,10 @@ just return strings from the view functions Flask will convert them into response objects for you. If you explicitly want to do that you can use the :meth:`~flask.make_response` function and then modify it. +Sometimes you might want to set a cookie at a point where the response +object does not exist yet. This is possible by utilizing the +:ref:`deferred-callbacks` pattern. + For this also see :ref:`about-responses`. Redirects and Errors