djangoannotationsdjango-class-based-views

Django QuerySet Annotations - More efficient way?


In a class Based ListView I want to annotate each Post with the number of comments (this method works)

def get_context_data(self, *args, **kwargs):
    context = super().get_context_data(*args, **kwargs)


    context['approved_comments'] = Comment.objects.values('post_id').annotate(c_count=Count('post_id')).filter(approved=True)
    return context

in my Template I do the following (which feels really inefficient)

{% for comment in approved_comments %}
    {% if post.id == comment.post_id %}
        {{comment.c_count}}
            
    {% endif %}
{% endfor %}

This gets me the result I want but I'm struggling to find a better way to deliver this since this seems really redundant.

I was trying to find a SO question that already deals with this but haven't found one so if you could point me to the link that would be helpful.

Thanks in advance :)


Solution

  • This is a quadratic JOIN, you should not do that. You can simply annotate the Posts with the number of approved comments in the view:

    from django.db.models import Count, Q
    from django.views.generic import ListView
    
    class MyListView(ListView):
        model = Post
        queryset = Post.objects.annotate(
            napproved_comments=Count('comment', filter=Q(comment__approved=True))
        )

    and then render this in the template with:

    {{ post.napproved_comments }}