Flash Messages
When a Bookshelf user submits the add-book form and gets redirected to the book list, there's a real gap left in the experience: nothing on the book-list page tells them the submission actually worked. Did the book get added? Did something go wrong? Flash messages are Flask's built-in answer — a one-time message queued up on one request and displayed on the very next one, right after a redirect.
Flask's flash() Function
Calling flash() queues a message to be shown on the next request that renders a
template. It takes the message text and an optional second argument — a category
— that lets you distinguish a success message from an error or a warning, so they can be
styled differently:
# app.py
from flask import flash, redirect, url_for
@app.route('/books/add', methods=['GET', 'POST'])
def add_book():
form = AddBookForm()
if form.validate_on_submit():
book = Book(title=form.title.data, pages=form.pages.data, user_id=current_user_id())
db.session.add(book)
db.session.commit()
flash('Book added successfully!', 'success')
return redirect(url_for('book_list'))
return render_template('add_book.html', form=form)
The category — 'success' here — is just a plain string you choose;
'danger' and 'warning' are common companions, chosen to line up
neatly with Bootstrap's alert classes, as the template below shows.
Displaying Flashed Messages with get_flashed_messages()
get_flashed_messages(with_categories=True) retrieves every message queued since
the last time it was called, paired with its category. Placing this loop in Bookshelf's
base.html — right below the nav bar, above {% block content %} —
means every page that extends the base template automatically gets flash-message support, with
no extra work needed on any individual page:
<!-- templates/base.html -->
<body>
<nav>...</nav>
{% with messages = get_flashed_messages(with_categories=true) %}
{% if messages %}
{% for category, message in messages %}
<div class="alert alert-{{ category }}">{{ message }}</div>
{% endfor %}
{% endif %}
{% endwith %}
{% block content %}{% endblock %}
</body>
Because the category string ('success', 'danger', 'warning')
is dropped straight into class="alert alert-{{ category }}", it lines up exactly
with Bootstrap's alert-success, alert-danger, and
alert-warning classes — a success message renders green, an error red, a warning
amber, with no extra conditional logic needed in the template at all.
Why Flash Messages Sit on Top of the Session
Flash messages aren't a separate feature bolted onto Flask — they're built directly on the
session mechanism from the previous lesson,
Sessions & Cookies. Calling flash()
appends the message into a list stored in session; calling
get_flashed_messages() reads that list back out — and then removes it from the
session immediately. This "read once" behavior is the entire point: it's what makes a flash
message a genuinely one-time notice rather than a sticky banner that reappears on every
following page. Refresh the book-list page after "Book added successfully!" first appears, and
it's gone — get_flashed_messages() already consumed and cleared it on the request
that displayed it.
Full Worked Example: Post/Redirect/Get with a Flash Message
This ties directly into the Post/Redirect/Get pattern from
Request & Response Objects — redirecting after a
successful POST so a page refresh doesn't resubmit the form. Flash messages are
the natural companion to that pattern, because the redirect target is exactly where the "read
once" message needs to surface:
Request 1: POST /books/add (form submitted)
-> book saved to the database
-> flash('Book added successfully!', 'success')
-> session now holds the queued message
-> 302 redirect to /books
Request 2: GET /books (browser follows the redirect)
-> get_flashed_messages() reads the message, clears it from session
-> "Book added successfully!" renders once, in green
Request 3: GET /books (a manual refresh)
-> get_flashed_messages() finds nothing left to read
-> no message appears — exactly as intended
Without the redirect in Request 1 — that is, if the view simply re-rendered the book list
directly from the POST handler — refreshing the resulting page would resubmit the
form itself (the classic "confirm form resubmission" browser warning). The redirect sidesteps
that entirely, and the flash message rides along for exactly one request, landing precisely on
the page the user actually ends up looking at.
User, Shelf, Book, and Tag models a real database to live in.