06 June, 2016 - 2 min read
Using Django ORM can lead to interesting problems - especially regarding logic operators.
Let's introduce a problem by writing a simple query to fetch all active Users.
> User.objects.filter(is_acitve=True) [<User: firstname.lastname@example.org>, <User: email@example.com>, <User: firstname.lastname@example.org>]
Naturally you can test if it's empty using Querysets
exists method, but what would happen if you try to compare it to something else. Let's say empty list?
> User.objects.filter(is_acitve=True) ==  False
What if we tried to take a look at inactive users and try the same comparison.
> User.objects.filter(is_acitve=False)  > User.objects.filter(is_acitve=False) ==  False
This is of course "normal" behavior since types don't match right?
> User.objects.all() == User.objects.all() False
The result may seem unexpected or even misleading but it actually works in favor of Django ORM - because
QuerySet class represents only lazy database lookup for a set of objects.
What happens when you have to compare two querysets for identity when it doesn't give proper results for most obvious examples? Here's couple ideas how to handle this problem.
Dict subclass for counting hashable items. Sometimes called a bag or multiset. Elements are stored as dictionary key and their counts are stored as dictionary values.
from collections import Counter Counter(queryset_a) == Counter(queryset_b)
Use simple set comparison
Very basic solution is to simply cast querysets to
set to perform comparison.
set(queryset_a) == set(queryset_b)
Important remark: don't cast to
list because then querysets can have different orders or may contain duplicates which will skew the results.