summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohn Hollingsworth <hollingsfsdf@gmail.com>2022-01-26 10:50:46 -0700
committerMariusz Felisiak <felisiak.mariusz@gmail.com>2022-02-08 08:33:24 +0100
commit8c407ee7f392ac44e9669658a473d6c486c388b3 (patch)
treec34b41152dd13705b2a7975c5094416656f76b6e
parent3714b44142dbf5b55b21530b2cc6d9cc2751da68 (diff)
[4.0.x] Fixed #32518 -- Doc'd that QuerySet.contains() should not be overused.
Thanks Tim McCurrach for the idea. Backport of d70b4bea18c96e518ce14dca96085e9265e8ebb6 from main
-rw-r--r--docs/topics/db/optimization.txt52
1 files changed, 30 insertions, 22 deletions
diff --git a/docs/topics/db/optimization.txt b/docs/topics/db/optimization.txt
index c9a0396b89..04f8cfc88b 100644
--- a/docs/topics/db/optimization.txt
+++ b/docs/topics/db/optimization.txt
@@ -261,44 +261,52 @@ But:
.. _overuse_of_count_and_exists:
-Don't overuse ``count()`` and ``exists()``
-------------------------------------------
+Don't overuse ``contains()``, ``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 ``subject`` attribute and a
-many-to-many relation to User, the following code is optimal::
+For example, assuming a ``Group`` model that has a many-to-many relation to
+``User``, the following code is optimal::
+
+ members = group.members.all()
- if display_emails:
- emails = user.emails.all()
- if emails:
- print('You have', len(emails), 'emails:')
- for email in emails:
- print(email.subject)
+ if display_group_members:
+ if members:
+ if current_user in members:
+ print("You and", len(members) - 1, "other users are members of this group.")
+ else:
+ print("There are", len(members), "members in this group.")
+
+ for member in members:
+ print(member.username)
else:
- print('You do not have any emails.')
+ print("There are no members in this group.")
It is optimal because:
#. Since QuerySets are lazy, this does no database queries if
- ``display_emails`` is ``False``.
+ ``display_group_members`` is ``False``.
-#. Storing ``user.emails.all()`` in the ``emails`` variable allows its result
- cache to be re-used.
+#. Storing ``group.members.all()`` in the ``members`` 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. If there
+#. The line ``if members:`` causes ``QuerySet.__bool__()`` to be called, which
+ causes the ``group.members.all()`` query to be run on the database. If there
aren't any results, it will return ``False``, otherwise ``True``.
-#. The use of ``len(emails)`` calls ``QuerySet.__len__()``, reusing the result
- cache.
+#. The line ``if current_user in members:`` checks if the user is in the result
+ cache, so no additional database queries are issued.
+
+#. The use of ``len(members)`` calls ``QuerySet.__len__()``, reusing the result
+ cache, so again, no database queries are issued.
-#. The ``for`` loop iterates over the already filled cache.
+#. The ``for member`` loop iterates over the result cache.
In total, this code does either one or zero database queries. The only
-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.
+deliberate optimization performed is using the ``members`` variable. Using
+``QuerySet.exists()`` for the ``if``, ``QuerySet.contains()`` for the ``in``,
+or ``QuerySet.count()`` for the count would each cause additional queries.
Use ``QuerySet.update()`` and ``delete()``
------------------------------------------