During my career as a codebase review bot I've seen many variations of this anti-pattern:
import models
queryset = models.Hound.objects.filter(is_asleep=True)
if queryset.count() > 0:
return "run away!"
else:
return "coast is clear"
An experienced Django dev may chuckle at this misapplication of count()
. I would never make that mistake! The thing is no dev is an island: over time devs change teams, inherit brownfield codebase that have accumulated tech debt, and will review code written by junior devs. It's therefore important to:
- avoid the mistake
- know how to effectively find and fix the mistake
- know how to communicate what Django does to make
exists
quicker
At the end of the post I will show how to effortlessly find and fix this anti-pattern.
Efficiency
Comparing queryset.count()
is less efficient than checking queryset.exists()
. The above example is a harder to read and less efficient variations of the following:
import models
queryset = models.Hound.objects.filter(pk=1)
if queryset.exists():
return "run away!"
else:
return "coast is clear"
The Django docs tells us to use querySet.count()
if you only want the count, and use queryset.exists()
if you only want to find out if at least one result exists.
The reason for this advice is because queryset.count()
performs an SQL operation that scans every row in the database table to calculate the sum. On the other hand queryset.exists()
simply reads a single record in the most optimized way:
- remove ordering
- remove grouping
- clear any dev-defined
select_related
anddistinct
from the queryset
Does your codebase misuse queryset.count?
Over time it's easy for tech debt to slip into your codebase. I can check that for you at django.doctor, or can review your GitHub PRs:
Or try out Django refactor challenges.
Top comments (0)