Search code examples
djangodjango-rest-frameworkmany-to-manyserialization

Handling ManyToMany field in DRF serializer


I have the following Django models:

class Article(models.Model):
  name = models.CharField(max_length=30, unique=True)
  tags = models.ManyToManyField(Tag)
  ...

class Tag(models.Model):
  name = models.CharField(max_length=30, unique=True)
  ...

I would like to be able to add a list of existing tags while creating Article via POST request, i.e.

{ 'name': 'My Org Name', 'tags': [1,2,3] }

Where [1, 2, 3] is a list of tag ids.

My serializer looks like this:

class ArticleSerializer(serializers.ModelSerializer):
     tags = serializers.PrimaryKeyRelatedField(many=True, read_only=True, allow_null=True)

     class Meta:
        model = Article
        fields = ('name', 'tags', ...)

     def create(self, validated_data):      
        # in create I will add tags, i.e. article.add(tag)    
        return Article.objects.create_article(**validated_data)

But then I would like to do some validation such that I know that tag ids that are passed in belong to existing Tag instances. So I tried adding the method below to my serializer.

def validate_tags(self, tags):
    if tags:
        for tag in tags:
            if not Tag.get(pk=tag):
                raise ValueError('Tag with id not found...')
    return tags

For some reason this validation method is not triggered. I'm trying to figure out the right way to handle adding tags to articles, any suggestions would be highly appreciated.


Solution

  • Since you are extending from the serializers ModelSerializer, you don't have to explicitly specify the 'tags' and you need not validate them. DRF do the the rest and creates fields and validation query according to the Model relation.

    class ArticleSerializer(serializers.ModelSerializer):
    
        class Meta:
            model = Article
            fields = ('name', 'tags', ...)
    
        def create(self, validated_data):
            tags = validated_data.pop('tags')
            article = Article.objects.create(**validated_data)
            article.tags.add(*tags)
            return article