user10073186
user10073186

Reputation:

Django Rest Framework: How to custom serializer.data's value?

When I call serializer.data, ManyToManyField will be displayed as list of ids,

but I want it to show tags's names instead of ids.

models.py

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


class Story(models.Model):
    title = models.CharField(max_length=100)
    tags = models.ManyToManyField(Tag)

serializers.py

from rest_framework import serializers

from stories.models import Story, Tag


class TagSerializer(serializers.ModelSerializer):
    class Meta:
        model = Tag
        fields = ('name',)


class StorySerializer(serializers.ModelSerializer):
    class Meta:
        model = Story
        fields = ('title', 'tags')

    def is_valid(self, raise_exception=False):
        self.initial_data['tags'] = self.get_tags_ids(self.initial_data['tags'])
        return super().is_valid(raise_exception)

    def get_tags_ids(self, tags):
        tags_ids = []
        for tag in tags:
            tag, _ = Tag.objects.get_or_create(name=tag)
            tags_ids.append(tag.id)

        return tags_ids

then in shell

In [1]: from stories.serializers import StorySerializer                         

In [2]: StorySerializer()                                                       
Out[2]: 
StorySerializer():
    title = CharField(max_length=100)
    tags = PrimaryKeyRelatedField(allow_empty=False, many=True, queryset=Tag.objects.all())

In [3]: TEST_DATA = { 
...:     'title': 'A story', 
...:     'tags': ['horror', 'action'], 
...: }                                                                       

In [4]: ser = StorySerializer(data=TEST_DATA)                                     

In [5]: ser.is_valid()                                                            
Out[5]: True

In [6]: ser.data                                                                  
Out[6]: {'title': 'A story', 'tags': [1, 2]}

How to let ser.data return {'title': 'A story', 'tags': ['horror', 'action']} instead?

Upvotes: 0

Views: 1504

Answers (1)

Cipher
Cipher

Reputation: 2132

You can use StringRelatedField link

In your model add this method

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

    def __str__(self) :  # <- This Method in Tag Model
        return self.name

Serializer.py

class StorySerializer(serializers.ModelSerializer):
    tags = serializers.StringRelatedField(many=True) # <- This line
    class Meta:
        model = Story
        fields = ('title', 'tags')

There is another way also to do it - Nested Serializer

Serializer.py

class TagsSerializer(serializers.ModelSerializer): # <- This Serializer
    class Meta:
        model = Tags
        fields = ('name',)

class StorySerializer(serializers.ModelSerializer):
    tags = TagsSerializer(many=True, read_only=True) # <- This line
    class Meta:
        model = Story
        fields = ('title', 'tags')

Upvotes: 0

Related Questions