Django rest framework serializing many to many field

116,212

Solution 1

You will need a TagSerializer, whose class Meta has model = Tag. After TagSerializer is created, modify the PostSerializer with many=True for a ManyToManyField relation:

class PostSerializer(serializers.ModelSerializer):
    tag = TagSerializer(read_only=True, many=True)

    class Meta:
        model = Post
        fields = ('tag', 'text',)

Answer is for DRF 3

Solution 2

This is what I did, let´s suppose a Book can have more than one author and an Author can have more than one book: On Model:

class Author(models.Model):
    name = models.CharField(max_length=100, default="")
    last_name = models.IntegerField(default=0)

class Book(models.Model):
    authors = models.ManyToManyField(Author, related_name="book_list", blank=True)
    name = models.CharField(max_length=100, default="")
    published = models.BooleanField(default=True)

On Serializers:

class BookSerializer(serializers.ModelSerializer):
    authors = serializers.PrimaryKeyRelatedField(queryset=Author.objects.all(), many=True)

    class Meta:
        model = Book
        fields = ('id', 'name', 'published', 'authors')


class AuthorSerializer(serializers.ModelSerializer):
    book_list = BookSerializer(many=True, read_only=True)

    class Meta:
        model = Author
        fields = ('id', 'name', 'last_name', 'book_list')

Solution 3

Adding to @Brian's answer "tags": [{"name": "tag1"}] can be simplified to "tags": ["tag1", "tag2",...] in this way:

class TagListingField(serializers.RelatedField):
 
     def to_representation(self, value):
         return value.name

class PostSerializer(serializers.ModelSerializer):
    tag = TagListingField(many=True, read_only=True)

    class Meta:
        ...

More info here: https://www.django-rest-framework.org/api-guide/relations/#custom-relational-fields

Solution 4

The default ModelSerializer uses primary keys for relationships. However, you can easily generate nested representations using the Meta depth attribute:

class PostSerializer(serializers.ModelSerializer):
    class Meta:
        model = Post
        fields = ("text", "tag")
        depth = 1 

As mentioned in the documentation :

The depth option should be set to an integer value that indicates the depth of relationships that should be traversed before reverting to a flat representation.

Solution 5

This works for me.

tag = TagSerializer(source="tag", read_only=True, many=True)
Share:
116,212

Related videos on Youtube

kengcc
Author by

kengcc

Updated on February 19, 2022

Comments

  • kengcc
    kengcc about 2 years

    How do I serialize a many-to-many field into list of something, and return them through rest framework? In my example below, I try to return the post together with a list of tags associated with it.

    models.py

    class post(models.Model):
        tag = models.ManyToManyField(Tag)
        text = models.CharField(max_length=100)
    

    serializers.py

    class PostSerializer(serializers.ModelSerializer):
        class Meta:
            model = Post
            fields = ("text", "tag"??)
    

    views.py

    class PostViewSet(viewsets.ReadOnlyModelViewSet):
        queryset = Post.objects.all()
        serializer_class = PostSerializer
    
    • kengcc
      kengcc over 8 years
      Using help from @Brian I manage to list the items in this form: "tags": [{"name": "tag1"}]. I would like to simplify it to list, is it possible: "tags": ["tag1", "tag2",...]
    • M. Dhaouadi
      M. Dhaouadi over 6 years
      use ` tags = serializers.SlugRelatedField(many=True,read_only=True, slug_field='title', //tag's fireld you want to show allow_null=True)` in PostSerializers
  • kengcc
    kengcc over 8 years
    it works!!! :D Any idea how to turn this serializer into just a comma separated list? class TagSerializer(serializers.ModelSerializer): class Meta: model = Tag fields = ('name')
  • kengcc
    kengcc over 8 years
    Right now, I get: "tags": [{"name": "tag1"}] I would like to simplify it to: "tags": ["tag1", "tag2",...]
  • Sachin Gupta
    Sachin Gupta over 8 years
    tags = serializers.ListField(source='tag'). This will get you the list of the str representation of each object of tag
  • getup8
    getup8 over 7 years
    What if you want to be able to update the tag through the Post? (e.g. not read_only) I'm getting weird behavior when I take away the read_only and try to PATCH an update to the tag field (I get an error about the tag already existing)
  • Pavel Vergeev
    Pavel Vergeev almost 7 years
    The read_only=True part is explained here: django-rest-framework.org/api-guide/relations/…
  • Vladimir Prudnikov
    Vladimir Prudnikov almost 6 years
    Try tags=serializers.CharSerializer(source='tag__name', many=True), note double underscore.
  • Kishan Mehta
    Kishan Mehta about 5 years
    Any idea how can we create authors on create of Book entity??
  • Jesus Almaral - Hackaprende
    Jesus Almaral - Hackaprende about 5 years
    Yes, it has to be done on the Views class, please post another question if you want a more detailed answer
  • bdoubleu
    bdoubleu almost 5 years
    For get_topics_list you could simplify to return list(instance.topics.values_list('desc', flat=True))
  • Олег Войтинський
    Олег Войтинський over 4 years
    You also must include a source attribute in the tag for example tag = TagSerializer(source="tag", read_only=True, many=True) Where source is your many-to-many field in a Model.
  • Piyush
    Piyush over 2 years
    After doing this how it will update tag array ???