Handling Django exceptions in the middleware

Prevent 500 error raised by exceptions by catching them in the middleware

A problem that many Django programmers face is uncaught exceptions triggering a 500 HTTP response. Even when you think you have covered all cases, there might be some external package that raises something else than you expect.

Most people solve this by writing a broad exception on a higher level to cope with these unexpected cases. This will do of course but it's better to write code based on what you know should happen, and have a backstop in case something unexpected happens.

One elegant way to cope with this is by catching these uncaught exceptions in your Django middleware. The example below shows how to catch an exception raised in a Django admin action:

exceptions.py

class CustomMiddlewareException(Exception):
    """This exception will be caught by middleware."""
    pass

admin.py

class CustomAdmin(ModelAdmin):
    actions = ('some_action',)
    def some_action(self, request, queryset):
        raise CustomMiddlewareException('Something happened while processing an action')

middleware.py

class ExceptionHandlingMiddleware:
    """Handle uncaught exceptions instead of raising a 500.
    """
    def __init__(self, get_response):
        self.get_response = get_response

    def __call__(self, request):
        return self.get_response(request)

    def process_exception(self, request, exception):
        if isinstance(exception, CustomMiddlewareException):
            # Show warning in admin using Django messages framework
            messages.warning(request, str(exception))
            # Or you could return json for your frontend app
            return JsonResponse({'error': str(exception)})

        return None  # Middlewares should return None when not applied

settings.py

MIDDLEWARE = [
    ...
    'your-project-name.middleware.ExceptionHandlingMiddleware',
]

Always make sure this is the last middleware in the list, Django will process the middlewares top-down and might handle other exceptions in more specialized middlewares. Your new middleware should be a last resort when all else has failed.

The middleware can also be used to return a 400 Bad Request. Several types of exceptions can be evaluated resulting in different responses. It might be a good idea to end with a broad exception to make sure everything will always be handled.