11

I am using django class based view and rest framework

object = self.get_object()

In Detail view if object does not exist and i do get request like

/user/10

then i get this response

{"detail": "not found"}

Now i want to customize that response

like

try:
   obj = self.get_object()
except:
   raise Exception("This object does not exist")

But thats not working

John Kaff
  • 1,905
  • 6
  • 18
  • 30

2 Answers2

9

We can implement a custom exception handler function that returns the custom response in case the object does not exist.

In case a object does not exist, Http404 exception is raised. So, we will check if the exception raised is Http404 and if that is the case, we will return our custom exception message in the response.

from rest_framework.views import exception_handler
from django.http import Http404

def custom_exception_handler(exc, context):
    # Call REST framework's default exception handler first,
    # to get the standard error response.
    response = exception_handler(exc, context)

    if isinstance(exc, Http404):  
        custom_response_data = { 
            'detail': 'This object does not exist.' # custom exception message
        }
        response.data = custom_response_data # set the custom response data on response object

    return response

After defining our custom exception handler, we need to add this custom exception handler to our DRF settings.

REST_FRAMEWORK = {
    'EXCEPTION_HANDLER': 'my_project.my_app.utils.custom_exception_handler'
}
Rahul Gupta
  • 46,769
  • 10
  • 112
  • 126
  • The exception is sailed within get_object() method of DRF . i think DRF has its own try except block there . is there any way that if i use try case on the top of that , like in my example , then my exception gets raised rather than DRF . – John Kaff Oct 14 '15 at 21:01
  • Yes, a `Http404` exception does get raised in the `get_object` method of DRF. You can use the method given in the ans and that should give you the same exception message in the response as you want. Just in case you want to override the `get_object` function, you can do that in your view by overriding the function. – Rahul Gupta Oct 15 '15 at 17:22
  • @RahulGupta Import Error: Could not import 'my_project.my_app.utils.custom_exception_handler' for API setting 'EXCEPTION_HANDLER'. AttributeError: module my_project.my_app.utils' has no attribute 'custom_exception_handler'. Could not solve it, please help – EngineSense Feb 17 '19 at 04:51
  • This example assumes that you have created an `utils.py` file inside `my_app` for `my_project`. Inside the `utils.py` file, you need to define your function. Please check if you have done the same. – Rahul Gupta Feb 17 '19 at 07:04
  • @EngineSense the import issue will come always because you don't have create __init__.py file in utils folder and you import it in your settings.py file, must have __init__.py file in utils folder. – Tanveer Ahmad Jan 05 '23 at 07:12
6

You could create a custom exception class as below, and it would raise APIException with a custom message and custom status_code

from rest_framework.serializers import ValidationError
from rest_framework import status



class CustomAPIException(ValidationError):
    """
    raises API exceptions with custom messages and custom status codes
    """
    status_code = status.HTTP_400_BAD_REQUEST
    default_code = 'error'

    def __init__(self, detail, status_code=None):
        self.detail = detail
        if status_code is not None:
            self.status_code = status_code


and in your views,

from rest_framework import status

try:
    obj = self.get_object()
except:
    raise CustomAPIException("This object does not exist", status_code=status.HTTP_404_NOT_FOUND)


The response will be like this
{"detail": "This object does not exist"}

NOTE

the detail parameter of CustomAPIException class takes str,list and dict objects. If you provide a dict object, then it will return that dict as exception response

UPDATE

As @pratibha mentioned, it's not possible to produce desired output if we use this exception class in Serializer's validate() or validate_xxfieldName() methods.

Why this behaviour ?
I wrote a similar answer in SO, here Django REST Framework ValidationError always returns 400

How to obtain desired output within the serializer's validate() method?
Inherit CustomAPIException from rest_framework.exceptions.APIException instead of from rest_framework.serializers.ValidationError
ie,

from rest_framework.exceptions import APIException


class CustomAPIException(APIException):
    # .... code
JPG
  • 82,442
  • 19
  • 127
  • 206