Skip to main content

Command Palette

Search for a command to run...

Handling Django exceptions in the middleware

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

Updated
Handling Django exceptions in the middleware
J

Made my first website in Microsoft Word at age 13, when I discovered the "save as html" button. Since then I've journeyed various computing languages, eventually settling for Python. I love diving into the core of my favorite framework - Django - discover and writing about new stuff.

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.

Deep into Django

Part 8 of 9

Django offers a great deal of hidden functionality - secret gems - if you know where to look. In this serie I am showing tips and tricks to get the most out of Django.

Up next

Prefetching in Django: less queries

Image (Daniel Bonilla): Casa Batlló by Gaudí in Barcelona, one of my favorite masterpieces in architecture.

More from this blog

D

Deep into Django

9 posts

I love diving into the core of my favorite framework - Django - discovering and writing about new tech stuff.