Go Back

Custom Exception Handler In Django Rest Framework

Creating Custom Exceptions Using Django Rest Framework
Posted by
Parimal P
Sep 1 2023

Exception handling is a crucial aspect of any web application, as it helps ensure that errors and unexpected situations are handled precisely.

In Django Rest Framework (DRF), a powerful and flexible toolkit for building Web APIs, you can create a custom exception handler to tailor error responses to your application’s needs.

In the current scenario, DRF is the de facto tool for creating REST APIs leveraing Django. It is a useful framework with effective functionalities.

In this blog post, we will explicitly create a custom exception handler that covers various exceptions and provides meaningful error messages to the clients.

We will even demonstrate the implementation of a custom exception handler with necessary code examples.

Understanding Django Rest Framework’s Exception Handling

Django Rest Framework (DRF) offers in-built exception handling that can be utilized to manage errors, mistakes, and exceptions in a RESTful API. Furthermore, exception handling in DRF allows you to intercept and customize how errors are presented to clients. By default, DRF provides a standard exception handler that formats exceptions into JSON responses.

However, it would help if you created a custom handler to tailor the error messages and structure to match your application’s requirements.

Read More:- Reasons to Choose Django For Web Development

Building a Custom Exception Handler

You can enable custom exception handling by building a handler function that transforms exceptions raised in the API views into response objects. This facilitates you to regulate the style of error responses utilized by the API. Let us build a custom exception handler with below explained steps.

Defining the Custom Exception Handler Function

Are you required to deal with and handle the exceptions yourselves? Here is how to do it better and precisely. To create a custom exception handler, first, you must define a function that takes two arguments: the exception (exc) and the context (context) in which the exception occurred.

The function will return an applicable error message. In our case, we will also include logic to handle different exceptions.

# exception_handler.py

from rest_framework.views import exception_handler
from .make_response import response
import logging
logger = logging.getLogger('django')

def custom_exception_handler(exc, context):
    """
    The function `custom_exception_handler` handles custom exceptions by mapping them to specific
    handlers and returning a response with the appropriate status code and message.
    
    :param exc: The `exc` parameter is the exception object that was raised. It contains information
    about the exception, such as its type, message, and traceback
    :param context: The `context` parameter in the `custom_exception_handler` function is a dictionary
    that contains information about the current request and view that raised the exception. It typically
    includes the following keys:
    :return: a response object.
    """
    try:
        exception_class = exc.__class__.__name__

        handlers = {
            'NotAuthenticated': _handler_authentication_error,
            'InvalidToken': _handler_invalid_token_error,
            'ValidationError': _handler_validation_error,
            # Add more handlers as needed
        }
        res = exception_handler(exc, context)
        if exception_class in handlers:
            # calling hanlder based on the custom  
            message = handlers[exception_class](exc, context, res)
        else:
            # if there is no hanlder is presnet
            message = str(exc)

        return response(data={}, status_code=res.status_code, message=message)
    except Exception as e:
        logger.error(str(e))

# ... (other handler functions)

Handling Different Types of Exceptions

This section will showcase you how to handle three types of exceptions: NotAuthenticated, InvalidToken, and ValidationError. You can extend this pattern to handle added exceptions as required.

# exception_handler.py

# ... (import statements)

# Existing handler functions

def _handler_authentication_error(exc, context, response):
    """
    The function returns a message indicating that an authorization token is not provided.
    
    :param exc: The `exc` parameter is the exception object that was raised during the authentication
    process
    :param context: The `context` parameter is a dictionary that contains additional information about
    the error that occurred. It can include details such as the request that caused the error, the user
    who made the request, or any other relevant information
    :param response: The `response` parameter is the HTTP response object that will be returned to the
    client. It contains information such as the status code, headers, and body of the response
    :return: the string "An authorization token is not provided."
    """
    return "An authorization token is not provided."

def _handler_invalid_token_error(exc, context, response):
    """
    The function handles an invalid token error by returning a specific error message.
    
    :param exc: The `exc` parameter represents the exception that was raised. In this case, it would be
    an invalid token error
    :param context: The `context` parameter is a dictionary that contains additional information about
    the error that occurred. It can include details such as the request that caused the error, the user
    who made the request, or any other relevant information
    :param response: The `response` parameter is the HTTP response object that will be returned to the
    client. It contains information such as the status code, headers, and body of the response
    :return: the string "An authorization token is not valid."
    """
    return "An authorization token is not valid."

def _handler_validation_error(exc, context, response):
    """
    The function `_handler_validation_error` handles validation errors by extracting the error code and
    value, and returning a custom error message based on the code.
    
    :param exc: The `exc` parameter is an exception object that is raised when a validation error
    occurs. It contains information about the error, such as the field that failed validation and the
    error code
    :param context: The `context` parameter is a dictionary that contains additional information about
    the exception that occurred. It can include details such as the request, view, and args that were
    being processed when the exception occurred
    :param response: The `response` parameter is the response object that will be returned by the
    handler. It is used to modify the response if needed
    :return: a message based on the validation error code.
    """
    key = list(list(exc.__dict__.values())[0].keys())[0]
    try:
        code = list(list(exc.__dict__.values())[0].values())[0][0].__dict__['code']
        value = list(list(exc.__dict__.values())[0].values())[0][0]

    except:
        code = list(list(exc.__dict__.values())[0].values())[0][0][0].__dict__['code']
        value = list(list(exc.__dict__.values())[0].values())[0][0][0]

    custom_msg_code = ["required", "null", "blank"]
    if code in custom_msg_code:
        message = f"{key} field is required"
    elif code:
        message = f"{value}"
    return message

# ... (other handler functions)

By default, NotAuthenticated exception leads to a direct response through the HTTP status code “401 Unauthenticated.” However, it may even lead to a “403 Forbidden” response, as per the authentication scheme in practice.

The ValidationError class is fundamentally utilized for field-based validation, and by using validator classes. It is raised while you call for serializer.is_valid with explicit raise_exception keyword argument.

Read More:- Why Should You Choose Python for Web Development?

Implementing the Custom Exception Handler in Django

To integrate the custom exception handler into your Django project, you must update the DRF settings in your settings.py file.

# settings.py

# ... (other settings)

# Configure the custom exception handler
REST_FRAMEWORK = {
    "EXCEPTION_HANDLER": "path.to.your.exception_handler.custom_exception_handler"
}

# ... (other settings)

It is significant to note that you should only raise custom exceptions when the error is somewhat that the customer can do something about, for instance, when the customer has used invalid input or other unauthorized requests.

If the error is a server-side problem, like a database connection error, utilizing the built-in exception handling offered by DRF or Django is better.

Read More:- Reasons Why Choose Python As Your Next Web Project

There are several and diverse properties accessible for inspecting the status of an API exception. You can leverage these properties to create custom exception handling for your in-hand and futuristic technology projects.

Conclusion

Custom exception handling can be a commanding tool when leveraged rightly in a Django Rest Framework project. With it, you can offer more comprehensive and useful error messages to your API clients and enhance the user experience of your API.

This blog post explored creating a custom exception handler for Django Rest Framework. We learned how to define a custom exception handler function, handle different types of exceptions with meaningful error messages, and integrate the custom handler into a Django project.

Customizing your exception-handling logic allows you to provide more user-friendly and informative error messages to clients, enhancing the overall user experience of your API. By tailoring error responses to your application’s needs, you can effectively communicate issues and promote a smoother interaction between your application and its users.

Moving Forward with Django App Development

Technostacks is a leading python development company and our teams have a proven experience in working with Django applications. Contact us for your next Django app development project.

X