diff options
Diffstat (limited to 'docs/topics/db/fetch-modes.txt')
| -rw-r--r-- | docs/topics/db/fetch-modes.txt | 138 |
1 files changed, 138 insertions, 0 deletions
diff --git a/docs/topics/db/fetch-modes.txt b/docs/topics/db/fetch-modes.txt new file mode 100644 index 0000000000..e76bb28a59 --- /dev/null +++ b/docs/topics/db/fetch-modes.txt @@ -0,0 +1,138 @@ +=========== +Fetch modes +=========== + +.. versionadded:: 6.1 + +.. module:: django.db.models.fetch_modes + +.. currentmodule:: django.db.models + +When accessing model fields that were not loaded as part of the original query, +Django will fetch that field's data from the database. You can customize the +behavior of this fetching with a **fetch mode**, making it more efficient or +even blocking it. + +Use :meth:`.QuerySet.fetch_mode` to set the fetch mode for model +instances fetched by a ``QuerySet``: + +.. code-block:: python + + from django.db import models + + books = Book.objects.fetch_mode(models.FETCH_PEERS) + +Fetch modes apply to: + +* :class:`~django.db.models.ForeignKey` fields +* :class:`~django.db.models.OneToOneField` fields and their reverse accessors +* Fields deferred with :meth:`.QuerySet.defer` or :meth:`.QuerySet.only` +* :ref:`generic-relations` + +Available modes +=============== + +.. admonition:: Referencing fetch modes + + Fetch modes are defined in ``django.db.models.fetch_modes``, but for + convenience they're imported into :mod:`django.db.models`. The standard + convention is to use ``from django.db import models`` and refer to the + fetch modes as ``models.<mode>``. + +Django provides three fetch modes. We'll explain them below using these models: + +.. code-block:: python + + from django.db import models + + + class Author(models.Model): ... + + + class Book(models.Model): + author = models.ForeignKey(Author, on_delete=models.CASCADE) + ... + +…and this loop: + +.. code-block:: python + + for book in books: + print(book.author.name) + +…where ``books`` is a ``QuerySet`` of ``Book`` instances using some fetch mode. + +.. attribute:: FETCH_ONE + +Fetches the missing field for the current instance only. This is the default +mode. + +Using ``FETCH_ONE`` for the above example would use: + +* 1 query to fetch ``books`` +* N queries, where N is the number of books, to fetch the missing ``author`` + field + +…for a total of 1+N queries. This query pattern is known as the "N+1 queries +problem" because it often leads to performance issues when N is large. + +.. attribute:: FETCH_PEERS + +Fetches the missing field for the current instance and its "peers"—instances +that came from the same initial ``QuerySet``. The behavior of this mode is +based on the assumption that if you need a field for one instance, you probably +need it for all instances in the same batch, since you'll likely process them +all identically. + +Using ``FETCH_PEERS`` for the above example would use: + +* 1 query to fetch ``books`` +* 1 query to fetch all missing ``author`` fields for the batch of books + +…for a total of 2 queries. The batch query makes this mode a lot more efficient +than ``FETCH_ONE`` and is similar to an on-demand call to +:meth:`.QuerySet.prefetch_related` or +:func:`~django.db.models.prefetch_related_objects`. Using ``FETCH_PEERS`` can +reduce most cases of the "N+1 queries problem" to two queries without +much effort. + +The "peer" instances are tracked in a list of weak references, to avoid +memory leaks where some peer instances are discarded. + +.. attribute:: RAISE + +Raises a :exc:`~django.core.exceptions.FieldFetchBlocked` exception. + +Using ``RAISE`` for the above example would raise an exception at the access of +``book.author`` access, like: + +.. code-block:: python + + FieldFetchBlocked("Fetching of Primary.value blocked.") + +This mode can prevent unintentional queries in performance-critical +sections of code. + +.. _fetch-modes-custom-manager: + +Make a fetch mode the default for a model class +=============================================== + +Set the default fetch mode for a model class with a +:ref:`custom manager <custom-managers>` that overrides ``get_queryset()``: + +.. code-block:: python + + from django.db import models + + + class BookManager(models.Manager): + def get_queryset(self): + return super().get_queryset().fetch_mode(models.FETCH_PEERS) + + + class Book(models.Model): + title = models.TextField() + author = models.ForeignKey("Author", on_delete=models.CASCADE) + + objects = BookManager() |
