Opened 5 years ago

Closed 5 years ago

#30473 closed Bug (duplicate)

len() and count() yield different results on randomly ordered QuerySet with annotation,

Reported by: Tobias Kunze Owned by: nobody
Component: Database layer (models, ORM) Version: dev
Severity: Normal Keywords:
Cc: Triage Stage: Unreviewed
Has patch: no Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description

I found myself with a QuerySet that is ordered randomly, and found that qs.count() and len(qs) return different results.

Additionally, since len(qs) sets some kind of result cache, any qs.count() called after len(qs) will return the same result as len(qs), which is an additional source of inconsistency and confusion. I'd argue that at the very least len(qs) should not be able to change the output of qs.count().

Minimal example here:

from django.db import models


class Article(models.Model):
    title = models.CharField(max_length=20)


class Review(models.Model):
    article = models.ForeignKey(to=Article, on_delete=models.CASCADE)
>>> from django.db import models
>>> a = Article.objects.create(title='Article')
(0.015) INSERT INTO "bar_article" ("title") VALUES ('Article'); args=['Article']
>>> Review.objects.create(article=a)
(0.026) INSERT INTO "bar_review" ("article_id") VALUES (1); args=[1]
<Review: Review object (1)>
>>> Review.objects.create(article=a)
(0.014) INSERT INTO "bar_review" ("article_id") VALUES (1); args=[1]
<Review: Review object (2)>
>>> result = Article.objects.annotate(review_count=models.Count('review')).order_by('?')
>>> result.count()
(0.000) SELECT COUNT(*) FROM (SELECT "bar_article"."id" AS Col1, COUNT("bar_review"."id") AS "review_count" FROM "bar_article" LEFT OUTER JOIN "bar_review" ON ("bar_article"."id" = "bar_review"."article_id") GROUP BY "bar_article"."id") subquery; args=()
1
>>> len(result)
(0.001) SELECT "bar_article"."id", "bar_article"."title", COUNT("bar_review"."id") AS "review_count" FROM "bar_article" LEFT OUTER JOIN "bar_review" ON ("bar_article"."id" = "bar_review"."article_id") GROUP BY "bar_article"."id", "bar_article"."title", RANDOM() ORDER BY RANDOM() ASC; args=()
2
>>> result.count()
2

Change History (1)

comment:1 by Mariusz Felisiak, 5 years ago

Resolution: duplicate
Status: newclosed
Summary: len() and count() yield different results on randomly ordered QuerySet with annotationlen() and count() yield different results on randomly ordered QuerySet with annotation,
Version: 2.2master

Thanks for the report. I would say that this is a duplicate of #26390, because this inconsistent behavior is due to the fact order_by('?') breaks queryset aggregations.

Note: See TracTickets for help on using tickets.
Back to Top