"connection.queries" returns nothing in Django
Question:
from django.db import connection, reset_queries
Prints: []
reset_queries()
p = XModel.objects.filter(id=id)
.values('name')
.annotate(quantity=Count('p_id'))
.order_by('-quantity')
.distinct()[:int(count)]
print(connection.queries)
While this prints:
reset_queries()
tc = ZModel.objects
.filter(id=id, stock__gt=0)
.aggregate(Sum('price'))
print(connection.queries)
I have changed fields names to keep things simple. (Fields are of parent tables i.e. __
to multiple level)
I was trying to print MySQL queries that Django makes and came across connection.queries
, I was wondering why doesn’t it prints empty with first, while with second it works fine. Although I am getting the result I expect it to. Probably the query is executed. Also am executing only one at a time.
Answers:
Because QuerySet
s in Django are lazy: as long as you do not consume the result, the QuerySet
is not evaluated: no querying is done, until you want to obtain non-QuerySet
objects like list
s, dict
ionaries, Model
objects, etc.
We can however not doe this for all ORM calls: for example Model.objects.get(..)
has as type a Model
object, we can not postpone that fetch (well of course we could wrap it in a function, and call it later, but then the “type” is a function, not a Model
instance).
The same with a .aggregate(..)
since then the result is a dict
ionary, that maps the keys to the corresponding result of the aggregation.
But your first query does not need to be evaluated. By writing a slicing, you only have added a LIMIT
statement at the end of the query, but no need to evaluate it immediately: the type of this is still a QuerySet
.
If you would however call list(qs)
on a QuerySet
(qs
), then this means the QuerySet
has to be evaluated, and Django will make the query.
The laziness of QuerySet
s also makes these chainings possible. Imagine that you write:
Model.objects.filter(foo=42).filter(bar=1425)
If the QuerySet
of Model.objects.filter(foo=42)
would be evaluated immediately, then this could result in a huge amount of Model
instances, but by postponing this, we now filter on bar=1425
as well (we constructed a new QuerySet
that takes both .filter(..)
s into account). This can result in a query that can be evaluated more efficiently, and for example, can result in less data that has to be transferred from the database to the Django server.
As the accepted answer says you must consume the queryset first since it’s lazy (e.g. list(qs)
).
Another reason can be that you must be in DEBUG mode (see FAQ):
connection.queries is only available if Django DEBUG setting is True
.
The documentation says QuerySets are lazy
as shown below:
QuerySets are lazy – the act of creating a QuerySet doesn’t involve
any database activity. You can stack filters together all day long,
and Django won’t actually run the query until the QuerySet is
evaluated. Take a look at this example:
>>> q = Entry.objects.filter(headline__startswith="What")
>>> q = q.filter(pub_date__lte=datetime.date.today())
>>> q = q.exclude(body_text__icontains="food")
>>> print(q)
Though this looks like three database hits, in fact it hits the
database only once, at the last line (print(q)). In general, the
results of a QuerySet aren’t fetched from the database until you “ask”
for them. When you do, the QuerySet is evaluated by accessing the
database. For more details on exactly when evaluation takes place, see
When QuerySets are evaluated.
from django.db import connection, reset_queries
Prints: []
reset_queries()
p = XModel.objects.filter(id=id)
.values('name')
.annotate(quantity=Count('p_id'))
.order_by('-quantity')
.distinct()[:int(count)]
print(connection.queries)
While this prints:
reset_queries()
tc = ZModel.objects
.filter(id=id, stock__gt=0)
.aggregate(Sum('price'))
print(connection.queries)
I have changed fields names to keep things simple. (Fields are of parent tables i.e. __
to multiple level)
I was trying to print MySQL queries that Django makes and came across connection.queries
, I was wondering why doesn’t it prints empty with first, while with second it works fine. Although I am getting the result I expect it to. Probably the query is executed. Also am executing only one at a time.
Because QuerySet
s in Django are lazy: as long as you do not consume the result, the QuerySet
is not evaluated: no querying is done, until you want to obtain non-QuerySet
objects like list
s, dict
ionaries, Model
objects, etc.
We can however not doe this for all ORM calls: for example Model.objects.get(..)
has as type a Model
object, we can not postpone that fetch (well of course we could wrap it in a function, and call it later, but then the “type” is a function, not a Model
instance).
The same with a .aggregate(..)
since then the result is a dict
ionary, that maps the keys to the corresponding result of the aggregation.
But your first query does not need to be evaluated. By writing a slicing, you only have added a LIMIT
statement at the end of the query, but no need to evaluate it immediately: the type of this is still a QuerySet
.
If you would however call list(qs)
on a QuerySet
(qs
), then this means the QuerySet
has to be evaluated, and Django will make the query.
The laziness of QuerySet
s also makes these chainings possible. Imagine that you write:
Model.objects.filter(foo=42).filter(bar=1425)
If the QuerySet
of Model.objects.filter(foo=42)
would be evaluated immediately, then this could result in a huge amount of Model
instances, but by postponing this, we now filter on bar=1425
as well (we constructed a new QuerySet
that takes both .filter(..)
s into account). This can result in a query that can be evaluated more efficiently, and for example, can result in less data that has to be transferred from the database to the Django server.
As the accepted answer says you must consume the queryset first since it’s lazy (e.g. list(qs)
).
Another reason can be that you must be in DEBUG mode (see FAQ):
connection.queries is only available if Django DEBUG setting is True
.
The documentation says QuerySets are lazy
as shown below:
QuerySets are lazy – the act of creating a QuerySet doesn’t involve
any database activity. You can stack filters together all day long,
and Django won’t actually run the query until the QuerySet is
evaluated. Take a look at this example:>>> q = Entry.objects.filter(headline__startswith="What")
>>> q = q.filter(pub_date__lte=datetime.date.today())
>>> q = q.exclude(body_text__icontains="food")
>>> print(q)Though this looks like three database hits, in fact it hits the
database only once, at the last line (print(q)). In general, the
results of a QuerySet aren’t fetched from the database until you “ask”
for them. When you do, the QuerySet is evaluated by accessing the
database. For more details on exactly when evaluation takes place, see
When QuerySets are evaluated.