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
At the end of the post I will show how to effortlessly find and fix this anti-pattern.
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
distinctfrom the queryset
Or try out Django refactor challenges.