Django 6.1 alpha dropped on May 20, 2026, and the headline feature is something Python developers have been quietly wanting for years: a built-in way to kill N+1 queries without manually tracking which fields you forgot to prefetch. The new QuerySet.fetch_mode() method does that — and it ships with a strict mode that raises an exception on any lazy field access, which is exactly the kind of blunt enforcement tool performance-focused teams actually need.
The N+1 Problem, Briefly
If you’ve written Django for more than a few months, you know the pattern. You fetch a list of objects, loop over them, access a related field, and Django fires a separate database query for each one. One hundred books, one hundred author queries, plus the original. 101 queries to render a page that looks fine in development and falls apart in production.
The existing fixes — select_related() and prefetch_related() — work, but they require you to know upfront which fields to optimize. Miss one in a template, add a field to a serializer, pass the queryset through a utility function that accesses something unexpected, and N+1 is quietly back. The discipline required to maintain this is real and ongoing.
Three Fetch Modes
Django 6.1 formalizes lazy field access into three explicit modes, set via QuerySet.fetch_mode():
- FETCH_ONE — The default. Django’s existing behavior: fetch one field for one instance per access. N+1 lives here.
- FETCH_PEERS — On first access to an unfetched field, Django batch-fetches that field for every instance from the same QuerySet. Two queries total, regardless of dataset size.
- RAISE — Any access to an unfetched field raises
FieldFetchBlockedimmediately. Zero lazy queries, by exception.
FETCH_PEERS in Practice
The usage is a one-liner chain:
books = Book.objects.fetch_mode(models.FETCH_PEERS)
for book in books:
print(f"{book.title} by {book.author.name}")
# Queries: 1 for books + 1 batch for authors = 2 total
Compare that to the default:
books = Book.objects.all()
for book in books:
print(f"{book.title} by {book.author.name}")
# Queries: 1 for books + N for authors = N+1
The mechanism: when you access book.author on the first instance, Django looks at the QuerySet that produced it, identifies all sibling instances, and fires a single query to fetch the missing field across all of them. The result is cached on each instance. Subsequent accesses in the loop hit the cache. It works for ForeignKey, OneToOneField, deferred fields from defer()/only(), and generic relations.
RAISE: Enforce the Policy
RAISE is the more interesting addition for teams that have lost patience with N+1 sneaking back in through template changes or serializer additions:
books = Book.objects.fetch_mode(models.RAISE)
for book in books:
print(book.author.name)
# Raises: FieldFetchBlocked("Fetching of Book.author blocked.")
Teams building high-traffic APIs can wrap critical endpoints with RAISE and write tests that verify zero lazy queries occur in a given code path. Data requirements become explicit, testable, and auditable. When something tries to access a field you didn’t explicitly load, you find out immediately — not after a production alert fires at 2 AM.
Set the Default at the Model Level
Rather than annotating every QuerySet call, apply FETCH_PEERS globally for a model via a custom manager:
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()
Now every Book.objects.all() or Book.objects.filter(...) uses FETCH_PEERS by default, project-wide. No per-view changes required.
What It Doesn’t Replace
prefetch_related() isn’t going anywhere. FETCH_PEERS doesn’t handle ManyToMany reverse relations through managers, and it doesn’t support filtered or ordered prefetches that require a Prefetch() object. If you need prefetch_related('tags', Prefetch('reviews', queryset=Review.objects.filter(rating__gte=4))), you still write that.
FETCH_PEERS is the automatic safety net. prefetch_related() is the explicit, precise tool. They coexist and solve different problems — one reactive and zero-maintenance, one deliberate and surgical.
Availability
Django 6.1 alpha 1 is available now for testing — not production-ready, but stable enough to evaluate fetch modes in a development environment. The full release notes cover the complete feature list, and the Fetch Modes documentation has all the details. Final release is expected in August 2026, requiring Python 3.12, 3.13, or 3.14. File bugs at code.djangoproject.com.
The alpha announcement is worth reading for the full context. If you’ve been manually hunting N+1 queries in every code review, this is worth knowing about before August.













