[Django]-Django REST Framework: Unique_together validation on Serializers

13👍

Unfortunately, Andreas’s answer is not quite complete, as it will not work in the case of an update.

Instead, you would want something more like:

def validate(self, attrs):
    field1 = attrs.get('field1', self.object.field1)
    field2 = attrs.get('field2', self.object.field2)

    try:
        obj = Model.objects.get(field1=field1, field2=field2)
    except StateWithholdingForm.DoesNotExist:
        return attrs
    if self.object and obj.id == self.object.id:
        return attrs
    else:
        raise serializers.ValidationError('field1 with field2 already exists')

This will work for PUT, PATCH, and POST.

👤JeffS

29👍

The ModelSerializer class has this functionality build-in, at least in djangorestframework>=3.0.0, however if you are using a serializer which doesn’t include all of the fields which are affected by your unique_togetherconstrain, then you’ll get an IntegrityError when saving an instance which violates it. For example, using the following model:

class Foo(models.Model):
    class Meta:
        unique_together = ('foo_a', 'foo_b')

    a = models.TextField(blank=True)
    b = models.TextField(blank=True)
    foo_a = models.IntegerField()
    foo_b = models.IntegerField(default=2)

and the following serializer and ViewSet:

class FooSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.Foo
        fields = ('a', 'b', 'foo_a')

class FooViewSet(viewsets.ModelViewSet):
    queryset = models.Foo.objects.all()
    serializer_class = FooSerializer


routes = routers.DefaultRouter()
routes.register(r'foo', FooViewSet)

if you try to save two instances with the same foo_a and foo_b set, you’ll get an IntegrityError. However, if we modify the serializer like this:

class FooSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.Foo
        fields = ('a', 'b', 'foo_a', 'foo_b')

you’ll then get a proper HTTP 400 BAD REQUEST status code, and the corresponding JSON descriptive message in the response body:

HTTP 400 BAD REQUEST
Content-Type: application/json
Vary: Accept
Allow: GET, POST, HEAD, OPTIONS

{
    "non_field_errors": [
        "The fields foo_a, foo_b must make a unique set."
    ]
}

I hope this to result helpful for you, even when this is a somewhat old-posted question 😉

👤seorc

25👍

I Needed this to override default message. Solved it by this.

from django.utils.translation import ugettext_lazy as _
from rest_framework import serializers, validators


class SomeSerializer(serializers.ModelSerializer):
  """
  Demostrating How to Override DRF UniqueTogetherValidator Message
  """

    class Meta:
        model = Some
        validators = [
            validators.UniqueTogetherValidator(
                queryset=model.objects.all(),
                fields=('field1', 'field2'),
                message=_("Some custom message.")
            )
        ]

Similarly, you can specify fields.

4👍

Yes, you can do it in the .validate() method of the serializer.

def validate(self, attrs):
    try:
        Model.objects.get(field1=attrs['field1'], field2=attrs['field2'])
    except Model.DoesNotExist:
        pass
    else:
        raise serializers.ValidationError('field1 with field2 already exists')

    return attrs

The unique constraint that you set in your model is for creating database constraints, not for validating.

2👍

Had the same problem and from this answer https://stackoverflow.com/a/26027788/6473175 I was able to get it to work but had to use self.instance instead of self.object.

def validate(self, data):
    field1 = data.get('field1',None)
    field2 = data.get('field2',None)

    try:
        obj = self.Meta.model.objects.get(field1=field1, field2=field2)
    except self.Meta.model.DoesNotExist:
        return data
    if self.instance and obj.id == self.instance.id:
        return data
    else:
        raise serializers.ValidationError('custom error message')

-1👍

Well this is kinda stupid and it’s very unlikely that anyone but me would make this mistake, but I had put the same model in two serializers class, as a consequence I had this problem

Hope my mistake help someone!

Leave a comment