Maintenance mode in Django

Maintenance mode in Django

To force a maintenance mode in Django, we need to redirect all traffic to a maintenance page, except for requests from specific IPs (like our own for management purposes). This can be achieved using middleware. Here's a basic approach:

  1. Create a maintenance middleware:

    • This middleware will intercept requests and redirect them to a maintenance page if a maintenance mode is active.
  2. Add a Maintenance View and Template:

    • Create a simple view and template for the maintenance page that users will see.
  3. Configure the middleware:

    • Add the middleware to your Django project settings.
  4. Activate/Deactivate Maintenance Mode:

    • We can activate maintenance mode by changing a variable, a file existence, or a database flag.

Here's a simple example to illustrate these steps:

Step 1: Create a Maintenance Middleware

We will create a file namedmaintainence_middleware.py in one of your apps:

from django.http import HttpResponse
from django.shortcuts import redirect

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

    def __call__(self, request):
        # Check if maintenance mode is active
        # This can be a setting in settings.py, a database flag, or checking for a file
        if hasattr(settings, 'MAINTENANCE_MODE') and settings.MAINTENANCE_MODE:
            # Allow access to certain IPs
            allowed_ips = ['123.456.78.90',]  # Your IP here
            if request.META.get('REMOTE_ADDR') in allowed_ips:
                return self.get_response(request)
            else:
                return redirect('maintenance_page')
        return self.get_response(request)

Step 2: Add a Maintenance View and Template

In our views.py:

from django.shortcuts import render

def maintenance_page(request):
    return render(request, 'maintenance.html')

Create a maintenance.html template in your templates directory with the appropriate HTML content.

Step 3: Configure the Middleware

In your settings.py, add the middleware to MIDDLEWARE:

MIDDLEWARE = [
    # ...
    'yourapp.maintenance_middleware.MaintenanceMiddleware',
    # ...
]

Step 4: Activate/Deactivate Maintenance Mode

In your settings.py:

# Set this to True to activate maintenance mode
MAINTENANCE_MODE = False

Step 5: You might want to:

  • Custom Maintenance Page: Customize the maintenance.html to inform our users about the maintenance and possibly provide an estimated time for when the service will be back.

  • Dynamic Activation: Instead of manually changing the settings.py, we could use an environment variable or a database setting that we can modify without deploying new code.

  • Logging Access Attempts: We might want to log attempts to access the site during maintenance for security and analysis.

  • Testing: Ensure we test this thoroughly in a staging environment before deploying it to production, especially the IP whitelisting part.

  • Notify Users in Advance: If possible, notify our users in advance about the planned maintenance.

By following these steps, you can implement a maintenance mode in your Django application that can be easily activated or deactivated as needed.

Step 6: Testing Maintainence Middleware

  1. Set up the test environment: This involves creating a test class that inherits from TestCase and setting up any necessary configurations.

  2. Test the middleware's behavior: You should test for both when maintenance mode is active and when it is not. Additionally, testing for access from allowed IPs and disallowed IPs is important.

Here's an example of how you might write these test cases:

1. Set Up the Test Environment

First, create a test file in your Django app's tests directory, e.g., test_middleware.py.

from django.test import TestCase, RequestFactory
from django.conf import settings
from yourapp.views import maintenance_page
from yourapp.maintenance_middleware import MaintenanceMiddleware

class MaintenanceMiddlewareTest(TestCase):

    def setUp(self):
        # The RequestFactory instance is a way to create request instances for use in testing.
        self.factory = RequestFactory()
        self.middleware = MaintenanceMiddleware(lambda x: x)

2. Test the Middleware's Behavior

Test When Maintenance Mode is Active

def test_maintenance_mode_active(self):
    # Set maintenance mode to active
    settings.MAINTENANCE_MODE = True

    # Create a request instance
    request = self.factory.get('/any-page')
    request.META['REMOTE_ADDR'] = '111.222.33.44'  # Non-allowed IP

    # Process the request through the middleware
    response = self.middleware(request)

    # Check that the response is a redirect to the maintenance page
    self.assertEqual(response.status_code, 302)
    self.assertEqual(response.url, '/maintenance-page')  # URL of your maintenance page

Test When Maintenance Mode is Inactive

def test_maintenance_mode_inactive(self):
    # Set maintenance mode to inactive
    settings.MAINTENANCE_MODE = False

    # Create a request instance
    request = self.factory.get('/any-page')

    # Process the request through the middleware
    response = self.middleware(request)

    # Check that the request passes through the middleware unaffected
    self.assertEqual(response.status_code, 200)

Test Access from Allowed IPs

def test_access_from_allowed_ip(self):
    # Set maintenance mode to active
    settings.MAINTENANCE_MODE = True

    # Create a request instance with an allowed IP
    request = self.factory.get('/any-page')
    request.META['REMOTE_ADDR'] = '123.456.78.90'  # Allowed IP

    # Process the request through the middleware
    response = self.middleware(request)

    # Check that the request passes through the middleware unaffected
    self.assertEqual(response.status_code, 200)

Additional Notes:

  • Make sure to replace 'yourapp' with the actual name of your Django app.

  • Modify the '/maintenance-page' with the actual URL of your maintenance page.

  • These tests assume that the normal response (when not in maintenance mode) has a status code of 200. Adjust according to your actual app's behavior.

  • Remember to reset any settings (like MAINTENANCE_MODE) after the test to avoid side effects on other tests.

By covering these scenarios, your test case will effectively validate the behavior of your maintenance middleware under different conditions.