Django Messages Framework: User Feedback Done Right

Clear feedback keeps a web application usable, and Django’s messages framework gives you a simple way to display notifications after actions such as form submissions or login events.
Instead of manually passing flags in the session or query string, you attach messages to the current request and render them in your templates.
The framework automatically manages storage and makes sure messages appear once and then disappear.

Enabling Messages In A Django Project

Most new Django projects already include the messages app and middleware in the default configuration.
The key pieces are the django.contrib.messages application in INSTALLED_APPS, the MessageMiddleware entry in MIDDLEWARE, and the messages context processor in your template settings.
Together, they ensure that view functions can add messages and that templates receive them through the messages variable.


# settings.py (excerpt)

INSTALLED_APPS = [
    "django.contrib.admin",
    "django.contrib.auth",
    "django.contrib.contenttypes",
    "django.contrib.sessions",
    "django.contrib.messages",
    "django.contrib.staticfiles",
    # your apps...
]

MIDDLEWARE = [
    "django.middleware.security.SecurityMiddleware",
    "django.contrib.sessions.middleware.SessionMiddleware",
    "django.middleware.common.CommonMiddleware",
    "django.middleware.csrf.CsrfViewMiddleware",
    "django.contrib.auth.middleware.AuthenticationMiddleware",
    "django.contrib.messages.middleware.MessageMiddleware",
    "django.middleware.clickjacking.XFrameOptionsMiddleware",
]

from django.conf import settings

TEMPLATES = [
    {
        "BACKEND": "django.template.backends.django.DjangoTemplates",
        "DIRS": [BASE_DIR / "templates"],
        "APP_DIRS": True,
        "OPTIONS": {
            "context_processors": [
                "django.template.context_processors.request",
                "django.contrib.auth.context_processors.auth",
                "django.contrib.messages.context_processors.messages",
            ],
        },
    },
]
    

With this configuration, every HttpRequest object has an associated message storage backend, and your templates can access pending messages without extra work.
If you created your project with the standard startproject command, you will usually find these entries already present, but it is worth checking before you start using messages in views.

See also  Django Form Validation: Custom Validators And Error Handling

Adding Messages In Views

Django provides an API for attaching messages with different levels such as debug, info, success, warning and error.
The most common case is to send a success message after processing a valid form and redirecting the user, or to send an error message when validation fails.
Using the built‑in shortcuts keeps the code concise and consistent across views.


from django.contrib import messages
from django.shortcuts import redirect, render
from .forms import ProfileForm

def profile_update(request):
    if request.method == "POST":
        form = ProfileForm(request.POST, instance=request.user.profile)
        if form.is_valid():
            form.save()
            messages.success(request, "Profile updated successfully.")
            return redirect("profile_detail")
        else:
            messages.error(request, "Please correct the errors below.")
    else:
        form = ProfileForm(instance=request.user.profile)

    return render(request, "accounts/profile_update.html", {"form": form})
    

In this example, messages follow the redirect, which is exactly the use case the framework was designed to support.
The user lands on the detail view and immediately sees confirmation that the update worked, or stays on the form with clear instructions to fix mistakes.
Compared to manually passing context between views, this approach is both cleaner and less error‑prone.

See also  How to optimize django database queries

Rendering Messages With Bootstrap Alerts

To display messages in templates, you iterate over the messages variable and render each entry as an alert.
Many projects pair Django’s message levels with Bootstrap alert classes so that success and error messages stand out visually.
With a small snippet in a base template, every page that extends it gains consistent notification behavior.


{% if messages %}
  <div class="messages">
    {% for message in messages %}
      <div class="alert {% if message.tags %}alert-{{ message.tags }}{% endif %}" role="alert">
        {{ message }}
      </div>
    {% endfor %}
  </div>
{% endif %}
    

Placing this block near the top of the body ensures that users see feedback immediately when the page loads.
By mapping Django’s success level to the Bootstrap alert-success class and error to alert-danger, you reinforce the meaning of the message through both text and color.
This also keeps styling centralized, so you can adjust the look of all notifications by editing a single CSS rule.

See also  How does Django connect to external database?

Custom Message Levels And Storage Backends

While the default configuration suits most applications, Django allows you to customize both the message levels and the storage backend.
You can introduce additional levels or change the mapping to numeric values, which is useful if you want to log certain messages or handle them differently in templates.
On the storage side, you can switch from session‑based storage to cookie‑based storage or implement your own backend if you have special requirements.


from django.contrib.messages import constants as message_constants

MESSAGE_LEVEL = message_constants.INFO

MESSAGE_TAGS = {
    message_constants.DEBUG: "secondary",
    message_constants.INFO: "info",
    message_constants.SUCCESS: "success",
    message_constants.WARNING: "warning",
    message_constants.ERROR: "danger",
}
    

This mapping lets you fine‑tune how each level appears in the user interface without changing view code.
You can also define a higher minimum level so that only important messages reach the templates, which reduces noise in complex applications.
Together, these options make the messages framework flexible enough for everything from small side projects to production systems.