Middleware flash messages in view methods
Flash messages are typically used when users perform an action (e.g. submit a form) and it's necessary to tell them if the action was successful or if there was some kind of error. Other times flash messages are used as one time notifications on web pages to tell users about certain events (e.g. site maintenance or special discounts). Figure 2-5 shows a set of sample flash messages.
Figure 2-5. Web page flash messages
By default, all Django projects are enabled to support flash messages. However, if you tweaked your project's
settings.py
file you may have inadvertently disabled flash messages.In order for Django flash messages to work you must ensure the following values are set in
settings.py
: The variableINSTALLED_APPS
has thedjango.contrib.messages
value, the variableMIDDLEWARE
has thedjango.contrib.messages.middleware.MessageMiddleware
value and thecontext_processors
list inOPTIONS
of theTEMPLATES
variable has thedjango.contrib.messages.context_processors.messages
value.
As you can see in figure 2-5 there can be different types of flash messages, which are technically known as levels. Django follows the standard Syslog standard severity levels and supports five built-in message levels described in table 2-9.
Table 2-9 Django built-in flash messages
Level Constant | Tag | Value | Purpose |
---|---|---|---|
DEBUG | debug | 10 | Development-related messages that will be ignored (or removed) in a production deployment |
INFO | info | 20 | Informational messages for the user |
SUCCESS | success | 25 | An action was successful, e.g. "Contact info was sent successfully" |
WARNING | warning | 30 | A failure did not occur but may be imminent |
ERROR | error | 40 | An action was not successful or some other failure occurred |
Add flash messages
Django flash messages are managed
on a per request basis and are added in view methods, as this is
the best place to determine whether flash messages are warranted.
To add messages you use the django.contrib.messages
package.
There are two techniques to add
flash messages with the django.contrib.messages
package, one is the generic add_message()
method and
the other are shortcuts methods for the different levels described
in table 2-9. Listing 2-31 illustrates the different
techniques.
Listing 2-31. Techniques to add Django flash messages
from django.contrib import messages # Generic add_message method # NOTE: Debug messages ignored by default messages.add_message(request, messages.DEBUG, 'The following SQL statements were executed: %s' % sqlqueries) messages.add_message(request, messages.INFO, 'All items on this page have free shipping.') messages.add_message(request, messages.SUCCESS, 'Email sent successfully.') messages.add_message(request, messages.WARNING, 'You will need to change your password in one week.') messages.add_message(request, messages.ERROR, 'We could not process your request at this time.') # Shortcut level methods # NOTE: Debug messages ignored by default messages.debug(request, 'The following SQL statements were executed: %s' % sqlqueries) messages.info(request, 'All items on this page have free shipping.') messages.success(request, 'Email sent successfully.') messages.warning(request, 'You will need to change your password in one week.') messages.error(request, 'We could not process your request at this time.')
The first set of samples in
listing 2-31 use the add_message()
method, where as
the second set uses shortcut level methods. Both sets of samples in
listing 2-31 produce the same results.
If you look closely at listing
2-31 you'll notice both DEBUG
level messages are preceded by the
line comment # NOTE: Debug messages ignored by default
. The Django
messages framework by default processes all messages above the
INFO
level (inclusive), which means DEBUG
messages -- being a lower level message threshold, as described in
table 2-9 -- are ignored even though they might be defined.
You can change the default Django
message level threshold to include all message levels or
inclusively reduce the default INFO
threshold. The
default message level threshold can be changed in one of two ways:
globally (i.e. for the entire project) in settings.py
with the MESSAGE_LEVEL
variable as illustrated in
listing 2-32 or on a per request basis with the
set_level
method of the
django.contrib.messages
package as illustrated in
listing 2-33.
Listing 2-32. Set default Django message level globally in settings.py
# Reduce threshold to DEBUG level in settings.py from django.contrib.messages import constants as message_constants MESSAGE_LEVEL = message_constants.DEBUG # Increase threshold to WARNING level in setting.py from django.contrib.messages import constants as message_constants MESSAGE_LEVEL = message_constants.WARNING
Listing 2-33. Set default Django message level on a per request basis
# Reduce threshold to DEBUG level per request from django.contrib import messages messages.set_level(request, messages.DEBUG) # Increase threshold to WARNING level per request from django.contrib import messages messages.set_level(request, messages.WARNING)
The first
MESSAGE_LEVEL
definition in listing 2-32 changes the
default message level to DEBUG
, which means all
message level definitions get processed, since DEBUG
is the lowest threshold. The second MESSAGE_LEVEL
definition in listing 2-32 changes the default message level to
WARNING
, which means message levels higher than
WARNING
(inclusive) are processed (i.e.
WARNING
and ERROR
).
The first set_level
definition in listing 2-33 changes the default request message
level to DEBUG
, which means all message level
definitions get processed, since DEBUG
is the lowest
threshold. The second set_level
definition in listing
2-33 changes the default message level to WARNING
,
which means message levels higher than WARNING
(inclusive) are processed (i.e. WARNING
and
ERROR
).
If you define both default
message level mechanisms at once, the default request message level
takes precedence over the default global message level definition
(e.g. if you define messages.set_level(request,
messages.WARNING)
, message levels above WARNING
(inclusive) are processed, even if the global
MESSAGE_LEVEL
variable is set to MESSAGE_LEVEL =
message_constants.DEBUG
to include all messages.
In addition to setting up flash
messages and knowing about the built-in threshold mechanism that
ignores messages from a certain level, it's also important you
realize the message definitions in listing 2-31 assume the Django
messages framework pre-requisites are declared in
settings.py
-- as described in the sidebar at the
beginning of this section.
Because you can end up
distributing a Django project to a third party and have no control
over the final deployment settings.py
file, the Django
messages framework offers the ability to silently ignore message
definitions in case the necessary pre-requisites aren't declared in
settings.py.
To silently ignore message definitions if
pre-requisites aren't declared you can add the
fail_silently=True
attribute to either technique that
adds messages, as illustrated in listing 2-34.
Listing 2-34 Use of the fail_silently=True attribute to ignore errors in case Django messages framework not installed
from django.contrib import messages # Generic add_message method, with fail_silently=True messages.add_message(request, messages.INFO, 'All items on this page have free shipping.',fail_silently=True) # Shortcut level method, with fail_silently=True messages.info(request, 'All items on this page have free shipping.',fail_silently=True)
Now that you know how to add messages and the important aspects to keep in mind when adding messages, lets take a look at how to access messages.
Access flash messages
The most common place you'll
access Django flash messages is in Django templates to display to
end users. As a short cut and thanks to the context processor
django.contrib.messages.context_processors.messages
Django flash messages are available on all templates through the
messages
variable. But before we get to an actual
template sample, lets take a quick look at the structure of Django
flash messages.
When you add a Django flash
message with one of the techniques described in the previous
section, Django creates an instance of the
storage.base.Message
class. Table 2-10 describes the
structure of the storage.base.Message
class.
Table 2-10 Django storage.base.Message structure
Attribute | Description | Example |
---|---|---|
message | The actual text of the message. | All items on this page have free shipping. |
level | An integer describing the type of the message (see Value column in table 2-9). | 20 |
tags | A string combining all the message tags (extra_tags and level_tag) separated by spaces. | info |
extra_tags | A string containing custom tags for this message, separated by spaces. | Empty, by default. |
level_tag | The string representation of the level. | info |
As you can see in table 2-10, there are several attributes which you can leverage to display in Django templates. Listing 2-35 shows the boilerplate template code you can use to display all flash messages set in a request.
Listing 2-35 Boilerplate code to use in Django template to display Django flash messages
{% if messages %} <ul class="messages"> {% for msg in messages %} <li> <div class="alert alert-{{msg.level_tag}}" role="alert"> {{msg.message}} </div> </li> {% endfor %} </ul> {% endif %}
Listing 2-35 starts by checking
if the messages
variable exists -- which contains all
flash messages -- if it does, then an HTML list is started with
<ul>
. Next, a loop is made over all the elements
in messages
, where each of these elements corresponds
to a storage.base.Message
instance. For each of these
elements, a list and section tag -- <li>
and
<div>
-- are created to output the
level_tag
attribute as a CSS class and the message
attribute as the <div>
content.
You can modify the boilerplate
code in listing 2-35 as you see necessary, for example to include
conditionals and output certain message levels or leverage some of
the other storage.base.Message
attributes, among other
things.
Note The HTML code in listing 2-35 uses the CSS classclass="alert alert-{{msg.level_tag}}"
which gets rendered intoclass="alert alert-info" or class="alert alert-success"
, depending on thelevel_tag
attribute. These CSS classes are part of the Bootstrap framework[6]. In this manner, you can quickly format flash messages to look like those presented in figure 2-5.
Although you'll commonly access
Django flash messages in Django templates, this doesn't mean you
can't access them elsewhere, such as view methods. You can also
gain access to Django flash messages in a request through the
get_messages()
method of the
django.contrib.messages
package. Listing 2-36
illustrates a code snippet with the use of the
get_messages()
method.
Listing 2-36 Use of get_messages() method to access Django flash messages
from django.contrib import messages the_req_messages = messages.get_messages(request) for msg in the_req_messages: do_something_with_the_flash_message(msg)
In listing 2-36 the
get_messages()
method receives the
request
as input and assigns the result to
the_req_messages
variable. Next, a loop is made over
all the elements in the_req_messages
, where each of
these elements corresponds to a storage.base.Message
instance. For each of these elements a call is made to the method
do_something_with_the_flash_message
to do something
with each flash message.
An important aspect to understand when accessing Django flash messages is the duration of the messages themselves. Django flash messages are marked to be cleared when an iteration occurs on the main messages instance and cleared when the response is processed.
For access in Django templates,
this means that if you fail to make an iteration in a Django
template like the one in listing 2-35 and flash messages are in the
request, it can lead to stale or phantom messages appearing
elsewhere until an iteration is made and a response is processed.
For access in Django view methods (i.e. using
get_messages()
), this has no impact because even
though you may make an iteration over the main messages instance --
therefore, marking messages to be cleared -- a response is not
processed in a Django view method, so messages are never cleared,
just marked to be cleared.