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. 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 For example, assuming an Email model that has a ``subject`` attribute and a
many-to-many relation to User, the following template code is optimal: many-to-many relation to User, the following 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 %}
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: It is optimal because:
#. Since QuerySets are lazy, this does no database queries if 'display_inbox' #. Since QuerySets are lazy, this does no database queries if
is False. ``display_emails`` is ``False``.
#. Use of :ttag:`with` means that we store ``user.emails.all`` in a variable #. Storing ``user.emails.all()`` in the ``emails`` variable allows its result
for later use, allowing its cache to be re-used. cache to be re-used.
#. The line ``{% if emails %}`` causes ``QuerySet.__bool__()`` to be called, #. The line ``if emails`` causes ``QuerySet.__bool__()`` to be called, which
which causes the ``user.emails.all()`` query to be run on the database, and causes the ``user.emails.all()`` query to be run on the database. If there
at the least the first line to be turned into an ORM object. If there aren't aren't any results, it will return ``False``, otherwise ``True``.
any results, it will return False, otherwise True.
#. The use of ``{{ emails|length }}`` calls ``QuerySet.__len__()``, filling #. The use of ``len(emails)`` calls ``QuerySet.__len__()``, reusing the result
out the rest of the cache without doing another query. 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 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 deliberate optimization performed is using the ``emails`` variable. Using
``QuerySet.exists()`` or ``QuerySet.count()`` at any point would cause ``QuerySet.exists()`` for the ``if`` or ``QuerySet.count()`` for the count
additional queries. would each cause additional queries.
Use ``QuerySet.update()`` and ``delete()`` Use ``QuerySet.update()`` and ``delete()``
------------------------------------------ ------------------------------------------