Dean Christian Armada
Dean Christian Armada

Reputation: 7384

Django Rest Framework Integity Error Catch

In Django Rest Framework I did the seriailizers, viewsets and routers approach. Whenever I POST with an intentional error in the API view of django rest framework it throws an Integrity Error. Is there a way to try and catch the errors like if there is no errors in the data just proceed to save however if there are errors throw a JSON Response with the list of errors like:

[{'section':'This field can not be blank', 'first_name':'This field can not be blank', 'middle_name':'This field can not be blank', 'last_name':'This field can not be blank'}]

models.py

from django.db import models

# Create your models here.
# class AuditTable(models.Model):
#   date_created = models.DateTimeField(auto_now_add=True)
    # test_field = models.CharField(max_length=50, null=True, blank=True)

class Section(models.Model):
    name = models.CharField(max_length=50, default=None)

    def __str__(self):
        return self.name

class Student(models.Model):
    section = models.ForeignKey(Section, default=None)
    first_name = models.CharField(max_length=50, default=None)
    middle_name = models.CharField(max_length=50, default=None)
    last_name = models.CharField(max_length=50, default=None)

    def __str__(self):
        full_name = "%s %s %s" % (self.first_name, self.middle_name, self.last_name)
        return full_name

serializers.py

from rest_framework import serializers
from rest_framework.response import Response

from . models import *

class SectionSerializer(serializers.ModelSerializer):
    class Meta:
        model = Section
        fields = ('name', )

class StudentSerializer(serializers.ModelSerializer):
    class Meta:
        model = Student
        fields = ('section', 'first_name', 'middle_name', 'last_name' )

viewsets.py

from rest_framework import viewsets

from . serializers import *
from . models import *

class SectionViewSet(viewsets.ModelViewSet):
    queryset = Section.objects.all()
    serializer_class = SectionSerializer

class StudentViewSet(viewsets.ModelViewSet):
    queryset = Student.objects.all()
    serializer_class = StudentSerializer

routers.py

from rest_framework import routers

from . viewsets import *

# Routers provide an easy way of automatically determining the URL conf.
router = routers.DefaultRouter()
router.register(r'students', StudentViewSet)
router.register(r'sections', SectionViewSet)

urls.py

from django.conf.urls import include, url

from . import views
from . routers import router

urlpatterns = [
    url(r'^$', views.index, name='testing_sample_index' ),
    url(r'^restful-form/$', views.web_service_form, name='testing_sample_web_service' ),

    url(r'^api/', include(router.urls)),
]

Upvotes: 5

Views: 3711

Answers (4)

Alvaro Rodriguez Scelza
Alvaro Rodriguez Scelza

Reputation: 4174

There is a better (in my opinion) solution: Django Rest Framework has a EXCEPTION_HANDLER that allows to customly handle exceptions not handled by default by it.

So, you have to:

  1. Configure DRF to specify which EXCEPTION_HANDLER to use. Got to your settings and add this:
    REST_FRAMEWORK = {
        'EXCEPTION_HANDLER': 'applications.utils.exception_handler'
    }
  1. Write the exception_handler function logic in the specified place (applications.utils in this case, but you can change that):
def exception_handler(exc, context):
    """
    Handle Django ValidationError as an accepted exception by DRF.
    For the parameters, see ``exception_handler``
    """
    if isinstance(exc, IntegrityError):
        exc = DRFValidationError(detail=exc)
    return drf_exception_handler(exc, context)

Now, whenever an IntegrityError is raised that DRF's serializer doesn't handle, this function will translate it to DRFValidationError which will be properly handled returning a 400 error with a proper data.

Upvotes: 1

raratiru
raratiru

Reputation: 9636

As per this answer, the solution would be:

viewsets.py

from django.db import IntegrityError    # Import IntegrityError
from rest_framework import viewsets
from rest_framework.exceptions import APIException  #Import APIException
from . serializers import *
from . models import *

class SectionViewSet(viewsets.ModelViewSet):
    queryset = Section.objects.all()
    serializer_class = SectionSerializer

    def create(self, request, *args, **kwargs):
        try:
            return super().create(request, *args, **kwargs)
        except IntegrityError as exc:
            raise APIException(detail=exc)

Upvotes: 7

Linovia
Linovia

Reputation: 20996

If you have an IntegrityError then your models aren't up to date with your database.

You should set uniqueness constraint to match your DB scheme.

Note that DRF can validate against unique constraints with the UniqueValidator

Upvotes: 5

Alex Morozov
Alex Morozov

Reputation: 5993

The default CreateMixin implementation raises an exception on any validation error. So you could write your own custom mixin...

class VerboseCreateModelMixin(object):
    """
    Create a model instance and return either created object or the validation errors.
    """
    def create(self, request, *args, **kwargs):
        serializer = self.get_serializer(data=request.data)
        if serializer.is_valid():
            self.perform_create(serializer)
            headers = self.get_success_headers(serializer.data)
            return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)
        else:
            return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

...and then use it in your ViewSet:

class SectionViewSet(VerboseCreateModelMixin, viewsets.ModelViewSet):
    queryset = Section.objects.all()
    serializer_class = SectionSerializer

Upvotes: 4

Related Questions