Opened 5 years ago

Closed 5 years ago

Last modified 20 months ago

#30655 closed Bug (wontfix)

len(queryset) and queryset.count() return different results.

Reported by: Sander Kleijwegt 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 (last modified by Sander Kleijwegt)

According to the queryset.count() documentation "you should always use count() rather than loading all of the record into Python objects and calling len() on the result" (see here), which seems to imply that the results of both functions should always be identical. Here is a simple example that shows the contrary.

Models:

class Bar(models.Model):
        pass

class Foo(models.Model):
        fooint = models.IntegerField()
        foobar = models.ForeignKey(Bar, related_name='foos', on_delete=models.CASCADE)

Shell Output:

>>> import django
>>> django.__version__
'2.2.3'
>>> from foobar.models import Foo, Bar
>>> bar = Bar.objects.create()
>>> foo1 = Foo.objects.create(fooint=1, foobar=bar)
>>> foo2 = Foo.objects.create(fooint=2, foobar=bar)
>>> Bar.objects.all().order_by('foos__fooint').count()
1
>>> len(Bar.objects.all().order_by('foos__fooint'))
2
>>> Bar.objects.all().order_by('foos__fooint')
<QuerySet [<Bar: Bar object (1)>, <Bar: Bar object (1)>]>

Change History (5)

comment:1 by Sander Kleijwegt, 5 years ago

Description: modified (diff)
Version: 2.02.2

comment:2 by Mariusz Felisiak, 5 years ago

Component: UncategorizedDatabase layer (models, ORM)
Resolution: wontfix
Status: newclosed
Summary: len(queryset) and queryset.count() return different resultslen(queryset) and queryset.count() return different results.
Version: 2.2master

Thanks for this report, but QuerySet.count() and len(QuerySet) can return different results.

count() calls SELECT COUNT(*) (as described in docs) without taking ordering into account, so in your case it returns the number of Bar's. On the other hand len() evaluates query with ordering by related table (one to many) that's why it returns a different number. This behavior is in Django since 9c52d56f6f8a9cdafb231adf9f4110473099c9b5.

comment:3 by Sander Kleijwegt, 5 years ago

So you are confirming that contrary to what the documentation says, you should NOT always use count() instead of len() ?

comment:4 by Mariusz Felisiak, 5 years ago

Nope. You should use count(), but you should know what you want to count. If you want to count the no. of Bar's then Bar.objects.all().count() is a obvious choice and it's natural for me that ordering doesn't impact this value. If you want to count the no. of Foo's then use Foo.objects.all().count(), etc.

comment:5 by Huub Bouma, 20 months ago

I think it's crazy that an order_by changes the amount of records in the resulting queryset..
Here's a workaround to achieve the sorting without duplicate records in the queryset:

from django.db.models import Max
Bar.objects.all().annotate(ordered_fooint=Max('foos__fooint')).order_by('ordered_fooint'))
Note: See TracTickets for help on using tickets.
Back to Top