Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Get count of related model efficiently in Django

I have a situation something like this (the actual code is bound up in a template, and omitted for brevity).

threads = Thread.objects.all()
for thread in threads:
    print(thread.comments.count())
    print(thread.upvotes.count())

I've managed to considerably reduce the total number of queries using Django's awesome prefetch_related method.

threads = Thread.objects.prefetch_related('comments').prefetch_related('upvotes')

However I'm wondering if this situation could be further optimized. From what I understand prefetch_related retrieves all of the data associated with the related models. Seeing as I only care about the amount of related models, and not about the models themselves, it seems like this query could be optimized further so that it doesn't retrieve a bunch of unnecessary data. Is there a way to do this in Django without dropping down to raw SQL?

like image 338
rectangletangle Avatar asked Apr 22 '14 04:04

rectangletangle


People also ask

How does Django handle many-to-many relationship?

Behind the scenes, Django creates an intermediary join table to represent the many-to-many relationship. By default, this table name is generated using the name of the many-to-many field and the name of the table for the model that contains it.

How do you count in Django?

Use Django's count() QuerySet method — simply append count() to the end of the appropriate QuerySet. Generate an aggregate over the QuerySet — Aggregation is when you "retrieve values that are derived by summarizing or aggregating a collection of objects." Ref: Django Aggregation Documentation.

What does Select_related do in Django?

In Django, select_related and prefetch_related are designed to stop the deluge of database queries that are caused by accessing related objects. In this article, we will see how it reduces the number of queries and make the program much faster.

What does .values do in Django?

The values_list() method allows you to return only the columns that you specify.


1 Answers

You're right, it's wasteful to fetch all that data from the database if all you want to do is get the count. I suggest annotation:

threads = (Thread.objects.annotate(Count('comments', distinct=True))
                         .annotate(Count('upvotes', distinct=True)))
for thread in threads:
    print(thread.comments__count)
    print(thread.upvotes__count)

See the annotation documentation for more information.

like image 171
Kevin Christopher Henry Avatar answered Oct 12 '22 05:10

Kevin Christopher Henry