Skip to content

Instantly share code, notes, and snippets.

@twidi
Created December 21, 2016 14:34
Show Gist options
  • Save twidi/9d55486c36b6a51bdcb05ce3a763e79f to your computer and use it in GitHub Desktop.
Save twidi/9d55486c36b6a51bdcb05ce3a763e79f to your computer and use it in GitHub Desktop.
Make Django Rest Framework correctly handle Django ValidationError raised in the save method of a model
"""
Sometimes in your Django model you want to raise a ``ValidationError`` in the ``save`` method, for
some reason.
This exception is not managed by Django Rest Framework because it occurs after its validation
process. So at the end, you'll have a 500.
Correcting this is as simple as overriding the exception handler, by converting the Django
``ValidationError`` to a DRF one.
"""
from django.core.exceptions import ValidationError as DjangoValidationError
from rest_framework.exceptions import ValidationError as DRFValidationError
from rest_framework.views import exception_handler as drf_exception_handler
def exception_handler(exc, context):
"""Handle Django ValidationError as an accepted exception
Must be set in settings:
>>> REST_FRAMEWORK = {
... # ...
... 'EXCEPTION_HANDLER': 'mtp.apps.common.drf.exception_handler',
... # ...
... }
For the parameters, see ``exception_handler``
"""
if isinstance(exc, DjangoValidationError):
exc = DRFValidationError(detail=exc.message_dict)
return drf_exception_handler(exc, context)
@pmburu
Copy link

pmburu commented May 3, 2020

Where could I be going wrong?

@jesselang
Copy link

Hard to say without seeing your adaption.

@mtolkacz
Copy link

Very helpful snippet. Thanks!

@rabbit-aaron
Copy link

If you think about it... this must have been done already.
As DRF has this ModelSerializer, which magically handles Django's validation just fine (like the ones raised by Model.clean method as well as the validators you specified for your fields)

Here's where the magic happens:

https://github.com/encode/django-rest-framework/blob/master/rest_framework/fields.py
rest_framework.fields.get_error_detail

and here is where it's used, very nicely implemented.

rest_framework.fields.Field.run_validators
https://github.com/encode/django-rest-framework/blob/master/rest_framework/fields.py

and rest_framework.serializers.Serializer.as_serializer_error
https://github.com/encode/django-rest-framework/blob/master/rest_framework/serializers.py

You should be able to handle it like this:

from django.core.exceptions import ValidationError as DjangoValidationError
from rest_framework.exceptions import ValidationError as DRFValidationError
from rest_framework.views import exception_handler as drf_exception_handler
from rest_framework.fields import get_error_detail


def exception_handler(exc, context):
    if isinstance(exc, DjangoValidationError):
        exc = DRFValidationError(detail=get_error_detail(exc))

    return drf_exception_handler(exc, context)

@iamunadike
Copy link

Please what is the context and how do I pass that to the django view?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment