0

I have been trying to fix this all morning but can't seem to find the issue. I have a specific API returning an IntegrityError duplicate key error in the form of the Django HTML error traceback instead of returning a detail error (JSON) on the form field.

I expect an error response to be in JSON form such that I can update the form using error.response.data but instead it is returning the classic Django error page with this snippet:

Exception Type: IntegrityError at /api/chats/ Exception Value: duplicate key value violates unique constraint "chats_chat_title_853c3234_uniq" DETAIL: Key (title)=(New Chat) already exists.

Model with a title field set to unique:

class Chat(TimestampedModel):
    """
    A chat between multiple users.
    """
    uuid = models.UUIDField(default=uuid4, null=False)
    title = models.CharField(
        max_length=255, null=False, unique=True, blank=False)

Serializer for the chat:

class ChatSerializer(serializers.ModelSerializer):
    title = serializers.CharField(max_length=255)

    def create(self, validated_data):
        """
        Creates a new Chat and adds the m2m employees to it
        """
        user = self.context['request'].user
        title = validated_data['title']

        # Add the user to the chat
        employee_ids = validated_data.pop("employee_ids")
        employee_ids.append(user.id)

        # Create and save the chat
        # Add the employees to the chat
        # Add the sender to the chat
        chat = Chat.objects.create(created_by=user, title=title)
        chat.employees.set(employee_ids)
        chat.employees.add(user)
        chat.save()
        return chat

And the ViewSet:

class ChatViewSet(MixedPermissionModelViewSet):
    lookup_field = 'uuid'
    queryset = Chat.objects.all()
    serializer_class = ChatSerializer
    permission_classes_by_action = {
        'list': [IsAuthenticated],
        'create': [IsAuthenticated],
        'update': [IsAuthenticated],
        'retrieve': [IsAuthenticated],
        'partial_update': [IsAuthenticated],
        'destroy': [IsAuthenticated]
    }

    def add_user_has_viewed(self, chat):
        if self.request.user in chat.employees.all():
            chat.has_viewed.add(self.request.user)
            chat.save()
        return chat

    def perform_create(self, serializer):
        chat = serializer.save()
        self.add_user_has_viewed(chat)

It is specific to this API during creation of an object. What am I missing?

1 Answer 1

1

This probably happens because the IntegrityError exception is rised at the ORM level and is not handled by Django Rest Framework's default exception handler. The best way I can think to fix this without rewriting the insides of ViewSet is to define a custom exception handler as described here.

from rest_framework.views import exception_handler
from rest_framework.response import Response
from rest_framework import status
from django.db import IntegrityError


def integrity_error_exception_handler(exc, context):
    response = exception_handler(exc, context)

    if isinstance(exc, IntegrityError) and not response:
        response = Response({'detail': 'Your error message'}, status=status.HTTP_400_BAD_REQUEST)

    return response

Then add this handler in settings.py

REST_FRAMEWORK = {
...
'EXCEPTION_HANDLER': 'utils.exceptions.integrity_error_exception_handler'
}

Also, you can use exc and context params to acces the original exception and view object in order to return more generic responses.

Sign up to request clarification or add additional context in comments.

3 Comments

The only difference between this API and my other ModelViewSet APIs is I override the serializers create() in this one. Does this mean that overriding that method is what is causing it?
@DavidAlford probably yes, but I don't want waste time debugging this to know exactly ;)
Fair enough. Just wondering if you know. I will debug it. Thanks!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.