Flask includes message flashing functionality as a core feature. The Flask documentation has two sentences which summarize the functionality well.
The flashing system basically makes it possible to record a message at the end of a request and access it on the next (and only the next) request. This is usually combined with a layout template to expose the message.
The Flask Web Development book describes Message Flashing on Pages 53-55. The Flask documentation also has a webpage titled Message Flashing, which also includes code examples.
You can call function flash
in a view function like this.
from flask import flash
# inside view function
flash('Log in successfully')
The base template calls get_flashed_messages
function and renders the
messages. The code below renders the messages as an un-ordered list.
{% with messages = get_flashed_messages() %}
{% if messages %}
<ul class="flashes">
{% for message in messages %}
<li>{{ message }}</li>
{% endfor %}
</ul>
{% endif %}
{% endwith %}
Flashing messages also support categories. You can pass a second
argument category to flash
method. The above example code
is modified to be like this.
flash('Log in failed', 'error')
{% with messages = get_flashed_messages(with_categories=True) %}
{% if messages %}
<ul class="flashes">
{% for category, message in messages %}
<li class="{{ category }}">{{ message }}</li>
{% endfor %}
</ul>
{% endif %}
{% endwith %}
The interesting part of the above code is that the function get_flashed_messages
is available to all
template html files. How does it happen? Let’s take a look at
Flask source code.
The flask/__init__.py
file has those two lines of code
(Lines 36 and 37).
from .helpers import flash
from .helpers import get_flashed_messages
The actual functions are defined on Lines 399 and 429 of helpers.py
file.
The code of of those two functions are not long. The message_flashed
variable in flash
function is a signal, which I will discuss in a
future article. The key _flashes
of session
dictionary has a value which
is a list. The list values are tuples. Here is an example [('message', 'log in'), ('error', 'log out')]
.
def flash(message, category="message"):
""" ... """
flashes = session.get("_flashes", [])
flashes.append((category, message))
session["_flashes"] = flashes
message_flashed.send(
current_app._get_current_object(), message=message,
category=category
)
def get_flashed_messages(with_categories=False, category_filter=()):
""" .... """
flashes = _request_ctx_stack.top.flashes
if flashes is None:
_request_ctx_stack.top.flashes = flashes = (
session.pop("_flashes") if "_flashes" in session else []
)
if category_filter:
flashes = list(filter(lambda f: f[0] in category_filter, flashes))
if not with_categories:
return [x[1] for x in flashes]
return flashes
The purpose of putting flashes
on the top of _request_ctx_stack.top
is that,
further calls in the same request to the function will return the same messages.
How does Jinja2 know get_flashed_messages
function? The code is in
create_jinja_environment
method of Flask class (Line 761 of app.py).
The function is added to the Jinja2 environment globals along with
other functions and variables.
def create_jinja_environment(self):
......
rv = self.jinja_environment(self, **options)
rv.globals.update(
url_for=url_for,
get_flashed_messages=get_flashed_messages,
config=self.config,
# request, session and g are normally added with the
# context processor for efficiency reasons but for imported
# templates we also want the proxies in there.
request=request,
session=session,
g=g,
)
rv.filters["tojson"] = json.tojson_filter
return rv