skip to content

Flash Notices

django.contrib.messages queues one-shot notices with success() or add_message() and clears them once a template iterates them. Interviewers probe storage backends and notices lost on redirects.

part ofDjangooverview, primer and where to startread it →
on this pageshow

explore

questions

4

In Django, how do you show a one-time 'Profile saved' notice on the page a user is redirected to?

level: juniorimportance: must knowfreq 55%

answer

  1. queue it before redirecting
  2. a shortcut per level
  3. middleware stores, template shows
  4. the loop consumes it

basics

~10 s

Call messages.success(request, 'Profile saved.') from django.contrib.messages before returning the redirect, and loop over messages in the base template; MessageMiddleware stores the notice across the redirect and iteration clears it.

solid answer

~40 s

With `django.contrib.messages` (enabled by `startproject`: the app, `MessageMiddleware` and the `messages` context processor), the view calls `messages.success(request, "Profile saved.")` and then returns `redirect("profile")`. The message is queued on the request's storage and saved by `MessageMiddleware` when the redirect response goes out. The next page's template, usually the base template, runs `{% for message in messages %}` and renders `{{ message }}` with `class="{{ message.tags }}"`. Iterating marks the messages used, so they are not stored again and a reload does not repeat them. Without the middleware, `add_message` raises `MessageFailure` unless `fail_silently=True`. For class-based views, `SuccessMessageMixin` sets the message after a valid form.

code

python · 13 lines
python
from django.contrib import messages
from django.shortcuts import redirect, render

from .forms import ProfileForm


def edit_profile(request):
    form = ProfileForm(request.POST or None, instance=request.user.profile)
    if request.method == "POST" and form.is_valid():
        form.save()
        messages.success(request, "Profile saved.")
        return redirect("profile")
    return render(request, "accounts/profile_form.html", {"form": form})

go deeper

for a junior

Remember the pattern: messages.success(request, text) before the redirect, and a for loop over messages in the base template.

for a middle

Explain the three parts startproject enables, why the redirect response carries the stored message, and that iteration is what clears it.

for a senior

Standardise notices across the project: one base-template loop, tags mapped to CSS, SuccessMessageMixin in CBVs, fail_silently in reusable apps.

for a principal

Decide how user feedback is delivered across server-rendered and script-driven pages so notices are neither lost nor duplicated between the two.

## What the messages framework is for `django.contrib.messages` delivers **one-time notices** ("Profile saved.", "Password too short.") from the request that creates them to the next page that displays them. The typical case is **Post/Redirect/Get**: a view handles a POST, saves something, and redirects. The redirect response renders no template, so the notice has to be stored somewhere between the two requests and shown once on the page the browser lands on. ## The three moving parts A new project from `startproject` already has all three enabled: | Part | Where | What it does | |---|---|---| | The app | `"django.contrib.messages"` in `INSTALLED_APPS` | Registers the framework | | The middleware | `django.contrib.messages.middleware.MessageMiddleware` in `MIDDLEWARE` | Attaches a storage object to each request and saves unread messages on the response | | The context processor | `django.contrib.messages.context_processors.messages` in the template `OPTIONS` | Puts `messages` (and `DEFAULT_MESSAGE_LEVELS`) into every template context | Without the middleware, calling `messages.success()` raises `MessageFailure` ("You cannot add messages without installing django.contrib.messages.middleware.MessageMiddleware") unless you pass `fail_silently=True`. ## Adding the notice in the view ```python from django.contrib import messages from django.shortcuts import redirect, render from .forms import ProfileForm def edit_profile(request): form = ProfileForm(request.POST or None, instance=request.user.profile) if request.method == "POST" and form.is_valid(): form.save() messages.success(request, "Profile saved.") return redirect("profile") return render(request, "accounts/profile_form.html", {"form": form}) ``` The shortcut functions `debug()`, `info()`, `success()`, `warning()` and `error()` all call `messages.add_message(request, level, message, extra_tags="", fail_silently=False)` with the matching level constant. Nothing is written at that moment: the message is **queued** on the request's storage, and `MessageMiddleware` stores it when the redirect response passes back through. ## Showing it in the template Put the loop in the base template so every page can display notices: ```django {% if messages %} <ul class="messages"> {% for message in messages %} <li class="{{ message.tags }}">{{ message }}</li> {% endfor %} </ul> {% endif %} ``` - `{{ message }}` renders the text. - `message.tags` is a space-separated string of any `extra_tags` plus the level tag (`"success"` here), ready for CSS classes. - `message.level` is the numeric level, useful with `DEFAULT_MESSAGE_LEVELS` for comparisons in templates. ## Why the notice shows exactly once Iterating the storage (the `{% for %}` loop) marks it as **used**. When the response for that page goes back through `MessageMiddleware`, the used messages are not stored again, so a reload does not show them. Only iteration counts: `{% if messages %}` checks the length without consuming anything. ## With a class-based view For an `UpdateView`, `django.contrib.messages.views.SuccessMessageMixin` adds the success message after `form_valid()` succeeds: ```python from django.contrib.messages.views import SuccessMessageMixin from django.views.generic import UpdateView class ProfileUpdateView(SuccessMessageMixin, UpdateView): fields = ["display_name", "bio"] success_url = "/profile/" success_message = "Profile saved for %(display_name)s." def get_object(self): return self.request.user.profile ``` `success_message` is interpolated with the form's `cleaned_data`. ## Common mistakes - **Rendering instead of redirecting after a successful POST.** The notice shows, but a browser refresh resubmits the form; redirect and let the message carry over. - **Putting the loop in only some templates.** A landing page that does not extend the base template never iterates `messages`, so the notice waits and appears on a later page. - **Passing the text in the query string.** It can be forged, bookmarked and logged; the messages framework signs cookie data and keeps session data server-side. - **Building a custom session key for notices.** You then have to remember to delete it; the framework already consumes on display. - **Adding the message in a script-driven endpoint.** A JSON response is never rendered with the template loop, so the notice appears on the next full page load instead. - **Forgetting `fail_silently=True` in a reusable app.** Projects that removed `MessageMiddleware` would otherwise get `MessageFailure`. ## Checklist 1. App, middleware and context processor enabled (they are by default). 2. `messages.success(request, ...)` **before** returning the redirect. 3. A `{% for message in messages %}` loop in a template every landing page extends. 4. Style with `message.tags`; do not build a custom session key for notices.

  • What happens if a Django view calls messages.success() but MessageMiddleware is not in MIDDLEWARE?
    `add_message()` looks for the storage the middleware attaches to the request. Without it, Django raises `MessageFailure` saying you cannot add messages without installing `MessageMiddleware`. Passing `fail_silently=True` suppresses the error, which reusable apps do so they work in projects that disabled messages.
  • How does Django's SuccessMessageMixin build its message for an UpdateView?
    After `form_valid()` succeeds, it calls `get_success_message(form.cleaned_data)`, which by default formats `success_message` with `%` against the cleaned data, so `"Saved %(display_name)s"` interpolates the field. If the result is non-empty it calls `messages.success()`. Override `get_success_message` to use other data, such as `self.object`.

saying these in an interview costs you the question

  • Store the notice in a custom session key and delete it manually
  • Pass the notice as a query-string parameter on the redirect
  • A message added before a redirect is lost because the redirect renders no template
  • Checking {% if messages %} is what clears the messages
  • messages.success() needs an explicit save call
open as a page

In Django's messages framework, what exactly consumes a message, and why can a notice survive redirects or appear on a later page?

level: middleimportance: should knowfreq 40%

basics

~20 s

In Django's messages framework only iterating the storage marks messages used; at the response, MessageMiddleware drops used messages and keeps unread ones, so a notice survives redirects and waits for the next page that loops over messages.

open as a page

In Django's messages framework, why does messages.debug() show nothing by default, and how do MESSAGE_LEVEL, MESSAGE_TAGS and extra_tags work?

level: middleimportance: should knowfreq 36%

basics

~10 s

Django records only messages at or above MESSAGE_LEVEL, which defaults to INFO (20), so DEBUG (10) messages are dropped when added. MESSAGE_TAGS overrides level-to-CSS tags, and extra_tags adds per-message tags to message.tags.

open as a page

Why is Django's default MESSAGE_STORAGE FallbackStorage, and what goes wrong if you use CookieStorage or SessionStorage alone?

level: seniorimportance: should knowfreq 28%

basics

~20 s

Django's FallbackStorage keeps short notices in a signed messages cookie and spills overflow into the session, so the common case needs no session write. CookieStorage alone drops the oldest messages past 2048 bytes; SessionStorage alone saves the session for every notice.

open as a page