11

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)

enter image description here

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.

2
  • That is logical: queries are performed lazy: only when you need them. Commented Jul 26, 2018 at 10:49
  • sorry couldn't get, can you please briefly explain? @WillemVanOnsem Commented Jul 26, 2018 at 10:51

3 Answers 3

15

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.

Sign up to request clarification or add additional context in comments.

2 Comments

Thanks for pointing out that it only works in debug mode. Where did you find this information?
3

Because QuerySets 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 lists, dictionaries, 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 dictionary, 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 QuerySets 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.

2 Comments

so is there a way to see the first one (Lazy)? because I was trying to solve this problem, and was thinking to add expiry date to values, and see if it actually produce right result.
You can obtain the query out of a queryset, with str(qs.query) so if you want to inspect the query, that might be a solution. Then you do not have to reset the queries as well. Or you can use list(qs) to force evaluation, like written in the answer.
0

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.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.