How to show UniqueTogetherValidator as field error instead of non-field error?

Viewed 272

I have a serializer like this:

class ContactSerializer(serializers.ModelSerializer):
    class Meta:
        model = Contact
        fields = (
            'account', 'first_name', 'last_name', 'email',
            'phone_number',
        )
        validators = [
            UniqueTogetherValidator(
                queryset=Contact.objects.all(),
                fields=['account', 'phone_number'],
                message='A contact with this phone number is already exists.',
            ),
        ]

API returns the unique together validator errors as non_field_errors. I want to show it in the specific field. In this case phone_number.

How can I do that?

1 Answers

I was able to overload the is_valid method to do this addition check. It is not the cleanest method since it will not run if there are existing errors but it is a work around. If anyone has a better solution would be great.

In this example, I have two fields I want to be unique together to the "project" field. I first run the inital validation so that I can access the the validated data. I then use the validator to check and add to the errors if need be.

class SequenceSerializer(serializers.ModelSerializer):

    def is_valid(self, raise_exception=False):
        """Run normal validations then check the fields are unique to the project."""
        if not super().is_valid(raise_exception=False):
            if raise_exception:
                raise ValidationError(self.errors)
            return False 
        
        for field in ["long_name", "short_name"]:
            # Run unique together validation
            validator = UniqueTogetherValidator(
                queryset=Sequence.objects.all(),
                fields=[field, "project",]
            )
            try:
                validator(self.validated_data, self)
            except ValidationError:
                # if it fails, remove field from validated data and add to errors
                del self.validated_data[field]
                self._errors[field] = [f"{field} must be unique to the project"]

        if self._errors and raise_exception:
            raise ValidationError(self.errors)
        
        return not bool(self._errors)

    class Meta:
        fields = ("uuid", "long_name", "short_name", "project")
        model = Sequence

Original is_valid method for ref: https://github.com/encode/django-rest-framework/blob/71e6c30034a1dd35a39ca74f86c371713e762c79/rest_framework/serializers.py#L212

Related