Django REST framework - unique_together, serializer validators, and only create if unique_together has been satisfied or update if not?

Viewed 82

I was wondering, I have the following within the Meta class of a model:

    class Meta:
        unique_together = ['owner', 'question', 'answer']

Within my API viewset, I perform the create as the following:

    def create(self, request, *args, **kwargs):

        serializer = self.get_serializer_class()(data=request.data, context={'request': request,}, many=False)

        serializer.is_valid(raise_exception=True)

        self.perform_create(serializer)

        headers = self.get_success_headers(serializer.data)

        return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)

Which is very formulaic Django REST Framework thus far.

The serializer is as follows:

class SurveyResultCreateOrUpdateSerializer(serializers.ModelSerializer):
    owner = serializers.PrimaryKeyRelatedField(
        read_only=True,
        default=serializers.CurrentUserDefault()
    )

    class Meta:
        model = SurveyResult
        fields = [
            'uuid', 'id', 'question', 'answer', 'owner',
        ]
        read_only_fields = [
            'uuid', 'id', 'owner',
        ]
        validators = [
            UniqueTogetherValidator(
                queryset=SurveyResult.objects.all(),
                fields=['owner', 'question', 'answer']
            )
        ]

So, when trying to create if an object where the validation fails, the error is raised. Brillaint. This is expected behaviour.

However, when the error is raised, I would like to immediately perform an update attempt if the unique together validation has been thrown...?

What would be the "pattern" way of doing this? Should I knock the serializer.is_valid() method to an if else statement, and on the else...or can I catch the raised exception only if the exception is a unique together error? Should I perform a separate update call from the client dependent on the response? Is there a way of creating an "perform_create_or_update()" function within the viewset? Is there a create_or_update viewset method?

My starting point would be something like this:

    def create(self, request, *args, **kwargs):

        serializer = self.get_serializer_class()(data=request.data, context={'request': request,}, many=False)

        try:
            serializer.is_valid(raise_exception=True)

            self.perform_create(serializer)

            headers = self.get_success_headers(serializer.data)

            return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)
        except exceptions.ValidationError as error:
            if 'unique' in error.get_codes()['non_field_errors'] and len(error.get_codes()['non_field_errors']) == 1:
                print("We should try and update")

            return Response(
0 Answers
Related