Unique validation on nested serializer in Django Rest Framework

I have a case where you have your own relationship of a nested serializer with a unique field. Example:

class GenreSerializer(serializers.ModelSerializer): class Meta: fields = ('name',) #This field is unique model = Genre class BookSerializer(serializers.ModelSerializer): genre = GenreSerializer() class Meta: model = Book fields = ('name', 'genre') def create(self, validated_data): genre = validated_data.pop('genre') genre = Genre.objects.get(**genre) return Book.objects.create(genre=genre, **validated_data) 

Problem: When I try to save a json object, for example {"name": "The Prince", "genre": {"name": "History"}} DRF tries to check the unique restriction of the genre object, and if there is a "History", me throws an exception, because the genre with the name "History" must be unique, and this is true, but I'm just trying to link the object, and not create together.

Many thanks!

+9
source share
3 answers

You must remove the unique validator for the nested serializer:

 class GenreSerializer(serializers.ModelSerializer): class Meta: fields = ('name',) #This field is unique model = Genre extra_kwargs = { 'name': {'validators': []}, } 

You can print your serializer before making sure that you have no other validators in this field. If you have, you will need to include them in the list.

Edit: if you need to provide a uniqueness constraint for creation, you must do this in the view after calling serializer.is_valid and before serializer.save .

+14
source

This is because the nested serializer ( GenreSerializer ) needs an instance of the object to correctly validate the unique constraint (such as placing the exclude GenreSerializer for the GenreSerializer queries used during validation), and by default the serializer does not transfer an instance of related objects to files. are nested serializers when the to_internal_value() method is to_internal_value() . Look here

Another way to solve this problem is to override the get_fields() method of the parent serializer and pass an instance of the associated object.

 class BookSerializer(serializers.ModelSerializer): def get_fields(self): fields = super(BookSerializer, self).get_fields() try: # Handle DoesNotExist exceptions (you may need it) if self.instance and self.instance.genre: fields['genre'].instance = self.instance.genre except Genre.DoesNotExist: pass return fields 
0
source

Together than removing UniqueValidator with

 'name': {'validators': []} 

You need to check the unique record yourself, ignoring the current object, so as not to get a 500 error when another person tries to keep the same name, something like this will work:

  def validate_name(self, value): check_query = Genre.objects.filter(name=value) if self.instance: check_query = check_query.exclude(pk=self.instance.pk) if self.parent is not None and self.parent.instance is not None: genre = getattr(self.parent.instance, self.field_name) check_query = check_query.exclude(pk=genre.pk) if check_query.exists(): raise serializers.ValidationError('A Genre with this name already exists .') return value 

The validate_<field> method is called to validate all of your fields, see the Documentation .

0
source

All Articles