Fetch modes¶
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 QuerySet.fetch_mode()
to set the fetch mode for model
instances fetched by a QuerySet
:
from django.db import models
books = Book.objects.fetch_mode(models.FETCH_PEERS)
Fetch modes apply to:
ForeignKey
fieldsOneToOneField
fields and their reverse accessorsFields deferred with
QuerySet.defer()
orQuerySet.only()
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¶
Referencing fetch modes
Fetch modes are defined in django.db.models.fetch_modes
, but for
convenience they’re imported into 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:
from django.db import models
class Author(models.Model): ...
class Book(models.Model):
author = models.ForeignKey(Author, on_delete=models.CASCADE)
...
…and this loop:
for book in books:
print(book.author.name)
…where books
is a QuerySet
of Book
instances using some fetch mode.
- 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.
- 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
QuerySet.prefetch_related()
or
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.
- RAISE¶
Raises a FieldFetchBlocked
exception.
Using RAISE
for the above example would raise an exception at the access of
book.author
access, like:
FieldFetchBlocked("Fetching of Primary.value blocked.")
This mode can prevent unintentional queries in performance-critical sections of code.
Make a fetch mode the default for a model class¶
Set the default fetch mode for a model class with a
custom manager that overrides get_queryset()
:
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()