[Django]-How does Django go about filtering an evaluated queryset?

6👍

Yes, the results get thrown out.

You can see this from the source: filter() calls _filter_or_exclude(), which calls _clone() and then adds to its query. _clone, you can see, doesn’t set the _result_cache attribute.

In general, it’s not really clear what it could possibly do to keep the common results. If it’s a complicated query with a small result set, it could be replaced by just issuing SQL that checks that the primary key is one of the results you’ve found, but that’s not always going to be more efficient, and in some situations it would confusingly mess with the semantics (if the DB changes in a way that affects the query results in the time between when it’s cached and when you do the additional filter).

If you want to force this behavior of saving the IDs manually, you can do that:

pks = SomeObject.objects.filter(...).values_list('pk', flat=True)
some_of_them = SomeObject.objects.filter(pk_in=pks).filter(...)
others = SomeObject.objects.filter(pk_in=pks).filter(...)

You can also of course just do the filtering in Python, e.g. by

 common = SomeObject.objects.filter(...)
 some_of_them = [m for m in common if m.attribute == 'foo']
 others = [m for m in common if m.other_attribute == 'bar']

(You could also use filter(lambda m: m.attribute == 'foo', common) if you preferred, or wrap the definition of common in list to be more explicit.)

Whether one of these or reissuing the query depends a lot on the size of the sets involved, the complexity of the filters, and what indices are present.

👤Danica

Leave a comment