When working with Django for my startup I performed in several occasions very complex migrations and maintenance tasks that required the site not to be accessible for a few minutes and sometimes even hours.

Even if the end user is not able to access the site, our team of superusers and admins should be able to access the site and perform the maintenance tasks.

Finally toggling the maintenance mode should be as easy as possible and should not require any code changes.

The Solution

First of all we need to create a new middleware that will check if the maintenance mode is enabled and if so, it will check if the user is a superuser, if not it will redirect the user to a maintenance page.

If the maintenance mode is not disabled and the user is trying to access the maintenance page, it will redirect the user to the home page.

What is a middleware?

A middleware will place itself between the request and the response. It’s lightweight and it’s a good place to perform checks and operations that are common to all the views.

Create the middleware

Create a new file called middleware.py in your app folder, for example config/middleware.py and add the following code:

import logging

from django.conf import settings
from django.shortcuts import redirect, reverse

logger = logging.getLogger(__name__)

class MaintenanceMiddleware:
    """
    Middleware to redirect users to the maintenance page if the site is in maintenance mode.

    How it works:
    - if superusers try to access the site, they can do so even if it's in maintenance mode
    - if maintenance mode is disabled, people requesting the maintenance page are redirected to the home page
    - if maintenance mode is enabled, people requesting any page other than the maintenance page are redirected to the maintenance page
    """

    def __init__(self, get_response):
        self.get_response = get_response

    def __call__(self, request):
        if request.user.is_superuser:
            return self.get_response(request)
        if not settings.MAINTENANCE_MODE and request.path == reverse("maintenance"):
            return redirect("base")
        if settings.MAINTENANCE_MODE and request.path != reverse("maintenance"):
            logger.info(
                "A user tried to access the site while it was in maintenance mode."
            )
            return redirect("maintenance")
        response = self.get_response(request)
        return response

Add the middleware to the settings

Now that we have created the middleware, we need to add it to the settings and create a new environment variable to enable or disable the maintenance mode.

Open the settings.py file and add the following code:

MIDDLEWARE = [
    # ...
    "config.middleware.MaintenanceMiddleware",
    # ...
]

MAINTENANCE_MODE = env.bool("MAINTENANCE_MODE", default=False)

Create a new view

We could create a function based view, but let’s Django do the heavy lifting and create a template view, the template view sole purpose is to render a template with context data if needed, a perfect fit for our maintenance page.

Open the main urls.py file and add the following code:


from django.urls import path

urlpatterns = [
    # ...
    path("maintenance/",TemplateView.as_view(template_name="maintenance.html"),
name="maintenance",
    ),
    # ...
]

Create the maintenance template

Again let’s keep it simple and reuse this codepen to create a simple maintenance page and make it Django friendly.

{% load i18n %}
<!doctype html>
<title>{% trans 'Under Maintenance' %}</title>
<link href="https://fonts.googleapis.com/css?family=Open+Sans:300,400,700" rel="stylesheet">
<style>
  html, body { padding: 0; margin: 0; width: 100%; height: 100%; }
  * {box-sizing: border-box;}
  body { text-align: center; padding: 0; background: #d6433b; color: #fff; font-family: Open Sans; }
  h1 { font-size: 50px; font-weight: 100; text-align: center;}
  body { font-family: Open Sans; font-weight: 100; font-size: 20px; color: #fff; text-align: center; display: -webkit-box; display: -ms-flexbox; display: flex; -webkit-box-pack: center; -ms-flex-pack: center; justify-content: center; -webkit-box-align: center; -ms-flex-align: center; align-items: center;}
  article { display: block; width: 700px; padding: 50px; margin: 0 auto; }
  a { color: #fff; font-weight: bold;}
  a:hover { text-decoration: none; }
  svg { width: 75px; margin-top: 1em; }
</style>
<article>
    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 202.24 202.24"><defs><style>.cls-1{fill:#fff;}</style></defs><title>Asset 3</title><g id="Layer_2" data-name="Layer 2"><g id="Capa_1" data-name="Capa 1"><path class="cls-1" d="M101.12,0A101.12,101.12,0,1,0,202.24,101.12,101.12,101.12,0,0,0,101.12,0ZM159,148.76H43.28a11.57,11.57,0,0,1-10-17.34L91.09,31.16a11.57,11.57,0,0,1,20.06,0L169,131.43a11.57,11.57,0,0,1-10,17.34Z"/><path class="cls-1" d="M101.12,36.93h0L43.27,137.21H159L101.13,36.94Zm0,88.7a7.71,7.71,0,1,1,7.71-7.71A7.71,7.71,0,0,1,101.12,125.63Zm7.71-50.13a7.56,7.56,0,0,1-.11,1.3l-3.8,22.49a3.86,3.86,0,0,1-7.61,0l-3.8-22.49a8,8,0,0,1-.11-1.3,7.71,7.71,0,1,1,15.43,0Z"/></g></g></svg>
    <h1>{% trans 'We will back soon' %}</h1>
    <div>
        <p>
          {% blocktrans %}
            We are currently performing scheduled maintenance. We should be back shortly. Thank you for your patience.
          {% endblocktrans %}
        </p>
        <p>&mdash; Front Desk Team</p>
    </div>
</article>

Test the maintenance mode

Now that we have everything in place, we can test the maintenance mode by setting the MAINTENANCE_MODE to True and try to access the site as a superuser and as a normal user.

If everything works as expected, you should be able to access the site as a superuser and you should be redirected to the maintenance page if you try to access the site as a normal user.

If however you try to access the maintenance page and the maintenance mode is disabled, you should be redirected to the home page.

Conclusion

In this article we have seen how to create a simple maintenance mode feature in Django, we have seen how to create a middleware, how to create a template view and how to create a simple template with a very simple code that can be improved and customized to fit your needs.

References


I hope this article was helpful, if you have any question or suggestion, feel free to reach out to me on :

You can use my articles with no cost or attribution, feel free to edit them and make them your own.