Search code examples
djangodjango-rest-frameworkdjango-serializer

DRF ListAPIView return manytomany value names instead of pk


I have a Post Model contains tags field that have ManyToManyField to categories Model,

when i call REST ListAPIView all post tags returns in pk I have tried to override list function in ListAPIView and map all tags_names for every post but this takes a huge time and destroys the performance

I hope/Believe there something built in for this case

models.py

class Post(models.Model):
    author = models.ForeignKey(User, on_delete=models.CASCADE)
    title = models.CharField(max_length=256)
    content = RichTextField()
    tags = models.ManyToManyField(Categories)

    def __str__(self):
        return self.title



class Categories(models.Model):
    tag_name = models.CharField(max_length=256, unique=True)

    def __str__(self):
        return self.tag_name

    class Meta:
        ordering = ('tag_name',)
        unique_together = ('tag_name',)

views.py

from .models import Post
from .serializers import NewPostSerializer, PostSerializer


class NewPost(CreateAPIView):
    serializer_class = NewPostSerializer
    permission_classes = [IsAuthenticated, IsAdminUser]


class PostList(ListAPIView):
    queryset = Post.objects.all()
    serializer_class = PostSerializer

serializers.py

class NewPostSerializer(ModelSerializer):

    class Meta:
        model = Post
        fields = ['title', 'content', 'tags']

        read_only_fields = ['tags', 'author_id']

when i visit ListApiView link returned results would be like this:

[
    {
        "id": 30,
        "title": "post title test",
        "content": "lorem text",
        "author": 3,
        "tags": [
            8, # should be games
            3  # should be action
        ]
    }
]

Solution

  • To optimize performance you should use prefetch_related. This reduces the number of queries to your database to only 1 request to fetch all the related tags for all of your posts.

    class PostList(ListAPIView):
        queryset = Post.objects.prefetch_related('tags').all()
        serializer_class = NewPostSerializer
    

    Now for the serialization of your tags you have to create a new serializer.

    class TagSerializer(ModelSerializer):
    
        class Meta:
            model = Categories
            fields = ['name']
    

    You can then use this serializer in your NewPostSerializer.

    class NewPostSerializer(ModelSerializer):
        tags = TagSerializer(many=True, read_only=True)
    
        class Meta:
            model = Post
            fields = ['title', 'content', 'tags']
    
            read_only_fields = ['author_id']
    

    The results of this should be "tags": [{"name": "ABC"},{"name": "EFG"}].