Changed "Don't overuse count() or exists()" example to Python.

This commit is contained in:
Adam Johnson 2020-08-11 17:40:12 +01:00 committed by Mariusz Felisiak
parent a736baab92
commit 8a642b88c3
1 changed files with 23 additions and 31 deletions

View File

@ -260,47 +260,39 @@ Don't overuse ``count()`` and ``exists()``
If you are going to need other data from the QuerySet, evaluate it immediately.
For example, assuming an Email model that has a ``body`` attribute and a
many-to-many relation to User, the following template code is optimal:
.. code-block:: html+django
{% if display_inbox %}
{% with emails=user.emails.all %}
{% if emails %}
<p>You have {{ emails|length }} email(s)</p>
{% for email in emails %}
<p>{{ email.body }}</p>
{% endfor %}
{% else %}
<p>No messages today.</p>
{% endif %}
{% endwith %}
{% endif %}
For example, assuming an Email model that has a ``subject`` attribute and a
many-to-many relation to User, the following code is optimal::
if display_emails:
emails = user.emails.all()
if emails:
print('You have', len(emails), 'emails:')
for email in emails:
print(email.subject)
else:
print('You do not have any emails.')
It is optimal because:
#. Since QuerySets are lazy, this does no database queries if 'display_inbox'
is False.
#. Since QuerySets are lazy, this does no database queries if
``display_emails`` is ``False``.
#. Use of :ttag:`with` means that we store ``user.emails.all`` in a variable
for later use, allowing its cache to be re-used.
#. Storing ``user.emails.all()`` in the ``emails`` variable allows its result
cache to be re-used.
#. The line ``{% if emails %}`` causes ``QuerySet.__bool__()`` to be called,
which causes the ``user.emails.all()`` query to be run on the database, and
at the least the first line to be turned into an ORM object. If there aren't
any results, it will return False, otherwise True.
#. The line ``if emails`` causes ``QuerySet.__bool__()`` to be called, which
causes the ``user.emails.all()`` query to be run on the database. If there
aren't any results, it will return ``False``, otherwise ``True``.
#. The use of ``{{ emails|length }}`` calls ``QuerySet.__len__()``, filling
out the rest of the cache without doing another query.
#. The use of ``len(emails)`` calls ``QuerySet.__len__()``, reusing the result
cache.
#. The :ttag:`for` loop iterates over the already filled cache.
#. The ``for`` loop iterates over the already filled cache.
In total, this code does either one or zero database queries. The only
deliberate optimization performed is the use of the :ttag:`with` tag. Using
``QuerySet.exists()`` or ``QuerySet.count()`` at any point would cause
additional queries.
deliberate optimization performed is using the ``emails`` variable. Using
``QuerySet.exists()`` for the ``if`` or ``QuerySet.count()`` for the count
would each cause additional queries.
Use ``QuerySet.update()`` and ``delete()``
------------------------------------------