Flask message flashing | Learning Flask Ep. 17
Providing feedback and notifications to users using Flask’s flash function
Providing feedback to users of a web application is critical, from notifications and error messages to warnings and progress alerts.
If the application doesn’t the right level of feedback, chances are the user will become frustrated and end up having a bad experience.
Fortunately for us, Flask provides a simple way to send messages from the server to the client using flash
.
High level overview
Flask’s message flashing system allows us to record a message at any point withing a request, then display it at the start of the next request (and only the next request).
This is ideal for cases when we want to provide the user with some feedback based on the actions of their current request, take this scenario for example:
- User attempts to create an account, submitting a form with a username and password
- The password must be at least 10 characters long, but the one submitted is only 8
- Our application rejects the short password and we call
flash
, passing it a message to let the user know the password was too short - We redirect the user back to the account creation page
- The message is flashed to the user, then destroyed. Provising them with feedback to let them know
Working with flash
requires 2 main components:
- Calling the
flash
function and passing it a message from a route in our application - Checking for flashed messages in a template
Now you know a little more about what flash
does, it’s time for an example.
Using flash in a route
To work with flash
, we need to import it from Flask
from flask import flash
We’ll go ahead and create a route which we described in the scenario earlier. An account creation page:
@app.route("/sign-up", methods=["GET", "POST"])
def sign_up():
if request.method == "POST":
req = request.form
username = req.get("username")
email = req.get("email")
password = req.get("password")
if not len(password) >= 10:
flash("Password length must be at least 10 characters")
return redirect(request.url)
flash("Account created!")
return redirect(request.url)
return render_template("public/sign_up.html")
This is obviously not a real account creation page! It’s just to demonstrate message flashing (We’re returning the user back to the sign up page, even on succcess)
The part to pay attention to here:
if not len(password) >= 10:
flash("Password must be at least 10 characters")
return redirect(request.url)
We’re performing some basic validation on the length of the password that the user submitted. If it doesn’t pass, we call the flash
function and pass it a message:
flash("Password length must be at least 10 characters")
Now we need to add something to our template to check for flashed messages.
If you’re using a base template, it’s the ideal place to put it. We’re going to put the following in our public_template.html
base template. If not, place it somewhere your users will notice and doesn’t interfere with the rest of your HTML.
Getting flashed messages
Here’s the basic Jinja syntax for getting flashed messages:
{% with messages = get_flashed_messages() %}
{% if messages %}
{% for message in messages %}
<!-- Do something with the message -->
{{ message }}
{% endfor %}
{% endif %}
{% endwith %}
- We call
with messages = get_flashed_messages()
to register any flashed messages {% if messages %}
then checkes to see if there are any messages in the queue{% for message in messages %}
iterates over the messages (you can callflash
more then once to register several messages)- We can then do something with the contents of the message using
{{ message }}
As it is, this code isn’t going to produce anything pretty or useful, so let’s go ahead and add some HTML to our flashed messages.
Styling flashed messages
We’re using Bootstrap for styling but feel free to add your own CSS.
Any code inside the {% for message in messages %}
block will be repeated for the amount of flashed messages in the queue and will persist on the page until the next request.
Bootstrap comes with the alert
class, featuring a dismissable button to clear the alert, so we’ll go ahead and use that. Place it above the main tag of your HTML or just above the form in the sign_up.html
page:
{% with messages = get_flashed_messages() %}
{% if messages %}
{% for message in messages %}
<div class="alert alert-primary alert-dismissible fade show" role="alert">
<span>{{ message }}</span>
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
{% endfor %}
{% endif %}
{% endwith %}
If you now submit the form with less that 10 characters in the password field, you’ll see a nice dismissable alert at the top of your page!
But what if we need even more control over the style of our flashed messages? The good news is there’s a simple solution.
Flash categories
Flask’s Flash function takes up to 2 arguments, a message and a category:
flash("message", "category")
Both values are not fixed and you’ve already seen us pass a message into flash
, but what about category
?
We can provide any value we like as the category argument and access it in our template, meaning we can style flashed messages using the category passed in and drop it into our HTML.
Consider the following:
You have 3 levels of alerts you’d like to show to your user.
- Success (Green)
- Warning (Yellow)
- Danger (Red)
Using the category
argument, we can design flashes depending on the control flow and what feedback we want the user to see.
To access the flash category, we need to refactor our previous flash:
{% with messages = get_flashed_messages(with_categories=true) %}
{% if messages %}
{% for category, message in messages %}
<div class="alert alert-{{ category }} alert-dismissible fade show" role="alert">
<span>{{ message }}</span>
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
{% endfor %}
{% endif %}
{% endwith %}
- Passing
with_categories=true
toget_flashed_messages()
, we’re able to access the category of that flash {% for category, message in messages %}
unpacks the message and category, which we can then access within the for loop
Depending on your CSS, you should use {{ category }}
as a class in your alert. As you can see from our Bootstrap example, we’re using it to complete the alert-{{ category }}
, which will trigger a different color of the alert.
alert-success
will trigger a green alertalert-warning
will trigger an orange alertalert-danger
will trigger a red alert
Let’s refactor our route to include a category in the flash:
@app.route("/sign-up", methods=["GET", "POST"])
def sign_up():
if request.method == "POST":
req = request.form
username = req.get("username")
email = req.get("email")
password = req.get("password")
if not len(password) >= 10:
flash("Password must be at least 10 characters", "warning")
return redirect(request.url)
flash("Account created!", "success")
return redirect(request.url)
return render_template("public/sign_up.html")
We haven’t changed much, just added the 2 classes to our flash
functions:
flash("Password must be at least 10 characters", "warning")
flash("Account created!", "success")
If you now submit the sign up form with both less than and more than 10 characters, you’ll see the 2 different styles of flash.
Warning:
Success:
Filtering flashes
In many cases, you’ll want to have more than 1 style of alert. You may have a notification section, warning section and conformation alert in different parts of your templates and in varius styles.
Filtering flashes allows us to only trigger a flash based on the category it receives! meaning you’re not fixed to one style of alert.
Say you’d like to have success notifications show up on the top right of your app and error messages pop up as a modal in the middle of your app, you can create 2 separate flash triggers in your templates to handle each instance.
The syntax for filtering flashes is very similar, with only some minor tweaks:
{% with success = get_flashed_messages(category_filter=["success"]) %}
{% if success %}
{%- for message in success %}
<div class="alert alert-success alert-dismissible fade show" role="alert">
<span>{{ message }}</span>
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
{% endfor -%}
{% endif %}
{% endwith %}
- By passing
category_filter=["success"]
toget_flashed_messages()
, we only trigger the flash when a given category is passed into theflash
function.
Using filtering, we can create several flashes in our templates and we only need to pass in the actual message of the flash.
In this simple example, we’ll create 3 flash instances in our template, each with their own filter. One for success, one for warning and one for danger. We’ll space these 3 flashes over 3 columns.
<div class="row">
<div class="col-sm-4">
{% with errors = get_flashed_messages(category_filter=["success"]) %}
{% if errors %}
{%- for message in errors %}
<div class="alert alert-success alert-dismissible fade show" role="alert">
<span>{{ message }}</span>
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
{% endfor -%}
{% endif %}
{% endwith %}
</div>
<div class="col-sm-4">
{% with errors = get_flashed_messages(category_filter=["warning"]) %}
{% if errors %}
{%- for message in errors %}
<div class="alert alert-warning alert-dismissible fade show" role="alert">
<span>{{ message }}</span>
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
{% endfor -%}
{% endif %}
{% endwith %}
</div>
<div class="col-sm-4">
{% with errors = get_flashed_messages(category_filter=["danger"]) %}
{% if errors %}
{%- for message in errors %}
<div class="alert alert-danger alert-dismissible fade show" role="alert">
<span>{{ message }}</span>
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
{% endfor -%}
{% endif %}
{% endwith %}
</div>
</div>
You’ll see, we’re filtering the flashes using our 3 categories, with a specific flash for each category.
Calling all 3 categories of flash in our route:
flash("Success!", "success")
flash("This is a warning", "warning")
flash("DANGER DANGER", "danger")
Produces the following:
Wrapping up
Flashing provides us with a simple and flexible way to provide users with valuable feedback and can be styled freely.
Using filters allows us to customize flashes depending on the category passed to it, keeping users happy and notified!
Last modified · 28 Feb 2019
Source : .