Django Rest Framework : How to initialise & use custom exception handler? - python

DRF newbie here.
I'm trying to handle all exceptions within the project through a custom exception handler. Basically, what I'm trying to do is if any serializer fails to validate the data, I want to send the corresponding error messages to my custom exception handler and reformat errors accordingly.
I've added the following to settings.py.
# DECLARATIONS FOR REST FRAMEWORK
REST_FRAMEWORK = {
'PAGE_SIZE': 20,
'EXCEPTION_HANDLER': 'main.exceptions.base_exception_handler',
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.TokenAuthentication',
'rest_framework.authentication.SessionAuthentication'
)
}
But once I send an invalid parameter to any of the endpoints in the project, I still get a default error message of the DRF validator. (e.g. {u'email': [u'This field is required.']})
Errors raised on corresponding serializer's validate function, never reaches to my exception handler.
Here is an image of the Project Tree that I'm working on.
Am I missing something?
Thank you in advance.

To do that, your base_exception_handler should check when a ValidationError exception is being raised and then modify and return the custom error response.
(Note:
A serializer raises ValidationError exception if the data parameters are invalid and then 400 status is returned.)
In base_exception_handler, we will check if the exception being raised is of the type ValidationError and then modify the errors format and return that modified errors response.
from rest_framework.views import exception_handler
from rest_framework.exceptions import ValidationError
def base_exception_handler(exc, context):
# Call DRF's default exception handler first,
# to get the standard error response.
response = exception_handler(exc, context)
# check that a ValidationError exception is raised
if isinstance(exc, ValidationError):
# here prepare the 'custom_error_response' and
# set the custom response data on response object
response.data = custom_error_response
return response

Related

Django rest_framework custom errror handler

I want to write django custom error handler as in flask custom error handler .
Suppose i have 100 api's which gets the same error everytime let's say
json.decoder.JSONDecodeError
Sample code
def post(self, request):
if not request: return Response({"message": "Please enter credentials"})
input_param = json.load(request)
print(input_param)
return "something"
The above code will return json.decoder.JSONDecodeError if no params are passed in post request .
In flask this can be handled by writing custom error handler like
#app.errorhandler(json.decoder.JSONDecodeError)
def handle_marshmallow_validaton_errors(err):
return jsonify({"error": "Bad request"}), 400
In django is there any way we can write custom error handlers
Thanks in advance
I think you can use DRF custom exception handler https://www.django-rest-framework.org/api-guide/exceptions/#custom-exception-handling.
you can use it to check the spawn exception and return a proper response.

Django shortcut get_object_or_404 inside Django Rest framework Class Based Views

The get_object_or_404 shortcut function in Django when encountering an exception gives a very nice error message in the following format:
'No %s matches the given query.' % queryset.model._meta.object_name)
However, while using this inside a DRF 3.X Class based view, the final 404 response data has a very stripped down version, which is as follows:
{"detail": "Not found."}
As is evident, the DRF message is very generic with no information about the Model name.I am assuming that the DRF NotFound Exception class defined here strips down the message to its current bare minimum.
How can I get the original nice error message that Django returns in-spite of using it within a DRF Class Based View ?
The default exception handler for an APIView class is decided by the get_exception_handler method. Ie,
def get_exception_handler(self):
"""
Returns the exception handler that this view uses.
"""
return self.settings.EXCEPTION_HANDLER
In other words the handler function that an APIView class use by default is rest_framework.views.exception_handler(Which is specified on the default settings).
As per the DRF Doc,
By default DRF handle the REST framework APIException, and
alsoDjango's built-in Http404 and PermissionDenied exceptions.
Since in this case you want to customize the error message only for the Http404, you can create your own handler like this.
ie,
from rest_framework.views import exception_handler
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)
# Now overide the error message.
if response is not None and isinstance(exc, Http404):
# set the custom message data on response object
response.data['detail'] = exc.args[0] # exc.args[0] will have the message text,
return response
Then place this custom handler in your project settings.
REST_FRAMEWORK = {
'EXCEPTION_HANDLER': 'my_project.my_app.utils.custom_exception_handler'
}
Note: This solution is not tested.

How to capture exception in django rest framework with django middleware?

Django middleware have a process_exception hook which can be used to capture exception and handler.
But there is some problem while using Django restframe work
class ExceptionHandler(MiddlewareMixin):
#staticmethod
def process_exception(request, exception):
if isinstance(exception, ValidationError):
return Response(data=exception.messages, status=status.HTTP_400_BAD_REQUEST)
For example, I try to use above middleware to capture the ValidationError and return HTTP 400
But it will not work and raise below error
AssertionError: .accepted_renderer not set on Response
It turns out that the rest-framework view layer will add a .accepted_renderer to the response.
If I handle the exception outside view. This attribute will be missed and cause another exception.
So my question is: Is it wrong to handle exception in middleware when using django rest-framework?
What is the correct way to do ?
A better way to do this in Django Rest framework is to create a custom exception handler and replace the default exception handler with your custom handler. For more details on it you can check out the official documentation: http://www.django-rest-framework.org/api-guide/exceptions/#custom-exception-handling

How can I use rest serializer.ValidationError in django forms?

I am new to Django and rest_framework. I have a password complexity rules script for 'new user' page.
If my complexity requirements satisfy the needs, it will return true. Else it raises serialiser.ValidationError.
I duplicated Django's forget password mechanism to apply my password rules.
When it raises an error, application crashes like below.
Exception Type: ValidationError
Exception Value:
[u"The two password fields didn't match."]
Is it possible to use serializer errors as form errors {{ form.new_password1.errors }}?
It's possible to write your own custom exception handler to return the error in a preferred format.
The official documentation, at the bottom of the page, says:
The ValidationError class should be used for serializer and field validation, and by validator classes. It is also raised when calling
serializer.is_valid with the raise_exception keyword argument:
The generic views use the raise_exception=True flag, which means that you can override the style of validation error responses globally
in your API. To do so, use a custom exception handler, as described
above.
serializer.is_valid(raise_exception=True)
By default this exception results in a response with the HTTP status
code "400 Bad Request"
Please read here to create your custom handler.

Custom exception handler not working as documented in django-rest-framework

I'm trying to write a custom exception handler in django-rest-framework, and the code is the same as given in the example:
from rest_framework.views import exception_handler
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)
# Now add the HTTP status code to the response.
if response is not None:
response.data['status_code'] = response.status_code
return response
But on raising an exception from the view, this does not work, it instead throws this message:
custom_exception_handler() missing 1 required positional argument: 'context'
I've tried setting the first argument to None, like so:
def custom_exception_handler(exc, context=None):
But this happens:
exception_handler() takes 1 positional argument but 2 were given
So it seems rest_framework.views.exception_handler takes only one argument.
Indeed this is the case:
def exception_handler(exc):
"""
Returns the response that should be used for any given exception.
By default we handle the REST framework `APIException`, and also
Django's built-in `ValidationError`, `Http404` and `PermissionDenied`
exceptions.
Any unhandled exceptions may return `None`, which will cause a 500 error
to be raised.
"""
So my question is, is this a bug? Or am i missing something, and there is another way to do this?..
EDIT:
This has been confirmed officially by the rest_framework team. This has been added in the latest version so it seems using v3.0.2 will not reflect the new documentation.
https://github.com/tomchristie/django-rest-framework/issues/2737
We can raise exception from API View using APIException or create custom Exception class that extends APIException.
raise APIException("My Error Message")
Now Custom Exception handler is
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, APIException):
response.data = {}
response.data['error'] = str(exc)
elif isinstance(exc, MyCustomException):
response.data = {}
response.data['error'] = str(exc)
return response

Categories