# Exception handling in DRF

Exceptions are errors or abnormal conditions that occur during the execution of a program. In Django (and Python in general), exceptions are a way to handle and respond to errors that occur while running code.

Here are some common exceptions that can occur in Django:

* `FieldError`: occurs when there is an error with the definition of a model field, such as a missing or invalid attribute.
    
* `ObjectDoesNotExist`: raised when trying to retrieve an object from the database that doesn't exist.
    
* `ValidationError`: raised when data being validated does not meet the criteria specified by a validator.
    
* `PermissionDenied`: raised when a user tries to access a resource they don't have permission to access.
    
* `Http404`: raised when a requested URL does not exist.
    
* `SuspiciousOperation`: raised when a potentially malicious operation is attempted.
    
* `InternalServerError`: raised when there is a server error that cannot be handled by the application.
    

By handling exceptions properly, you can create more robust and reliable Django applications.

Django Rest Framework provides several ways to handle exceptions.

1. You can use the `APIView` class-based views and customize the `handle_exception()` method to handle any exception that occurs during the request processing.
    
    First, create your custom `APIView` subclass and override the `handle_exception()` method:
    
    ```python
    from rest_framework.views import APIView
    from rest_framework.response import Response
    from rest_framework import status
    
    class MyView(APIView):
        def handle_exception(self, exc):
            # Call the superclass's version of handle_exception()
            # to get the standard error response.
            response = super().handle_exception(exc)
    
            # Now customize the response as needed.
            if response is not None:
                response.data['status_code'] = response.status_code
                response.data['message'] = 'Oops! Something went wrong. Please try again later.'
    
            return response
    ```
    
    In this example, we're adding a custom `status_code` and `message` field to the response data.
    
    Use the `MyView` class as the view for a URL pattern in your [`urls.py`](http://urls.py) file:
    
    ```python
    from django.urls import path
    from myapp.views import MyView
    
    urlpatterns = [
        path('my-view/', MyView.as_view(), name='my-view'),
    ]
    ```
    
    That's it! Now any exceptions are thrown by your `MyView` API class will be handled by the custom `handle_exception()` method you defined.
    
2. You can use the `@api_view` decorator with the `@catch_exception` decorator to catch specific exceptions.
    
    Usage:
    
    First, import the necessary decorators from DRF:
    
    ```python
    from rest_framework.decorators import api_view, catch_exception
    ```
    
    Define your API view function and decorate it with the `@api_view` decorator:
    
    ```python
    @api_view(['POST'])
    def my_view(request):
        # ...
    ```
    
    Now decorate the same function with the `@catch_exception` decorator, passing in the exception you want to catch:
    
    ```python
    @api_view(['POST'])
    @catch_exception(SomeException)
    def my_view(request):
        # ...
    ```
    
    In this example, we're catching instances of `SomeException`.
    
    Within your view function, you can handle the exception as you see fit. Here's an example:
    
    ```python
    @api_view(['POST'])
    @catch_exception(SomeException)
    def my_view(request):
        try:
            # Attempt to do something that might raise SomeException
            result = do_something()
        except SomeException:
            # Handle the exception in some way
            return Response({'error': 'Something went wrong'}, status=status.HTTP_400_BAD_REQUEST)
    
        return Response({'result': result}, status=status.HTTP_200_OK)
    ```
    
    In this example, if `do_something()` raises a `SomeException`, we catch it and return a custom error response with a 400 status code.
    
    And that's it! You can use the `@catch_exception` decorator to selectively catch exceptions within your API views.
    
3. You can also use DRF's built-in exception handlers by adding them to the `EXCEPTION_HANDLER` dictionary in your [`settings.py`](http://settings.py) file.
    
    Here's a brief demo of how to use the built-in DRF exception handlers in [`settings.py`](http://settings.py):
    
    First, import the `exception_handler` function from the `rest_framework.views` module:
    
    ```python
    from rest_framework.views import exception_handler
    ```
    
    Next, define a custom function that will handle exceptions for your API. This function should take two arguments: the request object, and the exception object (if any). Here's an example:
    
    ```python
    def my_exception_handler(exc, context):
        # Call REST framework's default exception handler first,
        # to get the standard error response.
        response = exception_handler(exc, context)
    
        # Now customize the response with your own error message
        if response is not None:
            response.data['status_code'] = response.status_code
            response.data['message'] = 'Oops! Something went wrong. Please try again later.'
    
        return response
    ```
    
    In this example, we're simply adding a custom `status_code` and `message` field to the response data.
    
    Finally, add the `my_exception_handler` function to the `EXCEPTION_HANDLER` dictionary in your [`settings.py`](http://settings.py) file:
    
    ```python
    REST_FRAMEWORK = {
        'EXCEPTION_HANDLER': 'my_project.my_app.my_exception_handler'
    }
    ```
    
    Note that the `my_project.my_app` part of the value should be replaced with the path to your custom exception handler function.
    
    And that's it! Now any exceptions thrown by your API will be handled by the `my_exception_handler` function defined in your settings file.
    

Overall, DRF offers a lot of flexibility for handling exceptions in your API code.
