Summary. I get very slow queries using multiple queries and annotate against two queries for each element when counting related objects. Database - PostgreSQL 9.3.5.
I have a model that looks something like this:
class Collection(models.Model): have = models.ManyToManyField(Item, related_name='item_have', through='Have') want = models.ManyToManyField(Item, related_name='item_want', through='Want') added = models.DateTimeField() class Meta: ordering = ['-last_bump'] class Have(models.Model): item = models.ForeignKey(Item) collection = models.ForeignKey(Collection, related_name='have_set') price = models.IntegerField(default=0) class Want(models.Model): want = models.ForeignKey(Item) collection = models.ForeignKey(Collection, related_name='want_set') price = models.IntegerField(default=0)
And, in my opinion, I list these Collections, and I want to show the number of desires and opportunities in each of them, doing this, making an annotation:
class ListView(generic.ListView): model = Collection queryset = Collection.objects.select_related() paginate_by = 20 def get_queryset(self): queryset = super(ListView, self).get_queryset() queryset = queryset.annotate(have_count=Count("have", distinct=True), want_count=Count("want", distinct=True))
This, however, makes my request very slow! I have about 650 records in DB and django-debug-toolbar says that it makes 2 queries and averages about 400-500 ms. I tried with prefetch_related, but that does not make it faster.
I tried another, in the Collection model, I added this:
@property def have_count(self): return self.have.count() @property def want_count(self): return self.want.count()
and removed the annotation from my view. Instead, it makes a total of 42 database queries, but this is done in 20-25 ms.
What am I doing wrong with my annotation here? Shouldn't it be faster to perform counting in one query, and also to make many counting queries?