1
0
mirror of https://github.com/django/django.git synced 2025-10-24 14:16:09 +00:00
Files
django/docs/topics/db/fetch-modes.txt
Adam Johnson 6dc9b04018 Refs #28586 -- Copied fetch modes to related objects.
This change ensures that behavior and performance remain consistent when
traversing relationships.
2025-10-16 14:52:22 -04:00

144 lines
4.3 KiB
Plaintext

===========
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`
Django copies the fetch mode of an instance to any related objects it fetches,
so the mode applies to a whole tree of relationships, not just the top-level
model in the initial ``QuerySet``. This copying is also done in related
managers, even though fetch modes don't affect such managers' queries.
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()