Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django QuerySet evaluation on .all()

Tags:

django

I am trying to find out if it is safe to do the following:

items = MyModel.objects.filter(q)
if items:
    list(items)

I know that the QuerySet is being evaluated in the if statement, checking if the database returns an empty set. But I want to know if the value of that evaluation is reused when doing list(items). Is the QuerySet being evaluated here too, or is it using the previously evaluated one?

I know I could just do the following:

items = MyModel.objects.filter(q).all()
if items:
    list(items)

And this would result in one evaluation, but I am just trying to find out the behavior the first variation has. I have gone throught these pieces of doc (1 2) but couldn't really find a straight answer to this matter.

like image 822
dabadaba Avatar asked Feb 01 '26 10:02

dabadaba


2 Answers

No. Both will not execute twice (internally .filter(), .all() and .filter().all() are same). You can check it in django shell itself

from django.db import connection
print connection.queries

items = MyModel.objects.filter(q).all() #or MyModel.objects.filter(q)
if items:
    list(items)

print connection.queries

Then here is the magic of .all()

queryset = MyModel.objects.all() #no db hit, hit=0
print list(queryset)             #db hit,    hit=1
print list(queryset)             #no db hit, hit=1
print list(queryset.all())       #db hit,    hit=2
print list(queryset.all())       #db hit,    hit=3

That means .all() on an evaluated queryset will force db hit.

When a QuerySet is evaluated, it typically caches its results. If the data in the database might have changed since a QuerySet was evaluated, you can get updated results for the same query by calling all() on a previously evaluated QuerySet

like image 103
itzMEonTV Avatar answered Feb 03 '26 06:02

itzMEonTV


It will reuse it's cache, because when you do

if items:

It will call __bool__ method

def __bool__(self):
    self._fetch_all()
    return bool(self._result_cache)

So as you see inside __bool__ it does call _fetch_all. Which caches data

def _fetch_all(self):
    if self._result_cache is None:
        self._result_cache = list(self.iterator())
    if self._prefetch_related_lookups and not self._prefetch_done:
        self._prefetch_related_objects()
like image 39
Sardorbek Imomaliev Avatar answered Feb 03 '26 05:02

Sardorbek Imomaliev



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!