Search code examples
pythondjangodjango-rest-frameworkdjango-mptt

How to optimize N+1 SQL queries when serializing a post with mptt comments?


I have the following serializer for a detailed post:

class ArticleDetailSerializer(serializers.ModelSerializer):
    author = ArticleAuthorSerializer(read_only=True)
    comments = CommentSerializer(many=True, read_only=True)

    class Meta:
        model = Article
        fields = '__all__'

Comment Serializer:

class CommentSerializer(serializers.ModelSerializer):

    class Meta:
        model = Comment
        fields = '__all__'

    def get_fields(self):
        fields = super(CommentSerializer, self).get_fields()
        fields['children'] = CommentSerializer(many=True, required=False, source='get_children')
        return fields

When working with a list of comments, I get 2 sql queries if I work with get_cached_trees()

class CommentListAPIView(generics.ListAPIView):
    serializer_class = serializers.CommentSerializer
    queryset = Comment.objects.all().get_cached_trees()

But how do you get the same thing to work for an article with a list of comments?

class ArticleDetailAPIView(generics.RetrieveAPIView):
    serializer_class = serializers.ArticleDetailSerializer
    queryset = Article.custom.all()
    lookup_field = 'slug'

    def get_queryset(self):
        queryset = self.queryset.prefetch_related(Prefetch('comments', queryset=Comment.objects.all().get_cached_trees()))
        return queryset

I used prefetch_related() but it didn't work. I used Prefetch(), it gave me an error:

'list' object has no attribute '_add_hints'

I seem to be lost in the ability to optimize the mptt comments for the article. But if you use the same comments, rendering according to the documentation in the Django template, then this problem is not observed. I ask for your help, dear programmers and experts.


Solution

  • This kind of solution worked for me.

    class ArticleDetailSerializer(serializers.ModelSerializer):
        author = ArticleAuthorSerializer(read_only=True)
        comments = serializers.SerializerMethodField()
    
        class Meta:
            model = Article
            fields = '__all__'
    
        def get_comments(self, obj):
            qs = obj.comments.all().get_cached_trees()
            return CommentSerializer(qs, many=True).data
    

    This reduced all queries from 11 SQL to 4.