Django select_related and prefetch_related: Fix N+1 Queries
Learn how to eliminate N+1 queries in Django with select_related and prefetch_related, including practical examples, when to use each, and common pitfalls.
The N+1 query problem is one of the most common performance issues in Django applications. It happens when a queryset loads a collection of objects and then accesses a related object inside a loop, causing Django to run one extra query per object. select_related and prefetch_related solve this by loading related objects up front. This article explains how both methods work, when to use each one, and how to avoid common mistakes.
The N+1 Query Problem in Django ORM
Consider two models: an Author and a Book, where each book has a foreign key to an author.
from django.db import models class Author(models.Model): name = models.CharField(max_length=100) class Book(models.Model): title = models.CharField(max_length=200) is_published = models.BooleanField(default=False) author = models.ForeignKey(Author, on_delete=models.CASCADE)
If you retrieve all books and then access each book's author, Django executes one query for the books and one additional query per book to fetch its author. For 100 books, that is 101 queries. This is the N+1 problem: the initial query plus N dependent queries.
books = Book.objects.all() for book in books: print(book.author.name) # One query per book
The ORM lazily evaluates book.author, triggering a new database query each time. The fix is to tell Django to fetch the related objects in advance, using either select_related or prefetch_related.
How select_related Works
select_related works by creating a SQL JOIN and including the related object's fields in the same SELECT statement. It supports forward relationships: ForeignKey and OneToOneField.
books = Book.objects.select_related('author') for book in books: print(book.author.name) # No extra query
This executes a single query that joins the book and author tables. The related Author instance is populated in memory, so accessing book.author does not hit the database again.
select_related can traverse multiple levels of forward relations using double underscores:
books = Book.objects.select_related('author__profile')
This joins the book, author, and profile tables in one query. The more joins you add, the wider the result set becomes, which can increase memory usage and slow down the query itself. Use it only for relationships you actually access.
How prefetch_related Works
prefetch_related performs a separate query for each related lookup and then joins the results in Python. It is designed for reverse relationships and many-to-many fields, where a JOIN would produce duplicate parent rows.
class Author(models.Model): name = models.CharField(max_length=100) class Book(models.Model): title = models.CharField(max_length=200) author = models.ForeignKey(Author, on_delete=models.CASCADE, related_name='books')
Now, to fetch all authors and their books:
authors = Author.objects.prefetch_related('books') for author in authors: print(len(author.books.all())) # No extra query per author
Django runs one query for authors and another for all books that belong to those authors, then matches them in Python. After the prefetch, author.books.all() returns the cached list of related books. Note that author.books.count() does not rely on the prefetched list, so use the cached all() result instead if you want to avoid extra work.
prefetch_related works with ManyToManyField, reverse ForeignKey, and GenericRelation.
prefetch_related also supports nested prefetching:
authors = Author.objects.prefetch_related('books__publisher')
This fetches authors, then books, then publishers of those books, using three queries total.
Choosing Between select_related and prefetch_related
The decision depends on the relationship type and the shape of the data you need.
| Relationship Type | Method | SQL Behavior | Best For |
|---|---|---|---|
| ForeignKey | select_related | JOIN, single query | Accessing the related object |
| OneToOneField | select_related | JOIN, single query | Accessing the related object |
| Reverse ForeignKey | prefetch_related | Separate query, Python join | Accessing a set of related objects |
| ManyToManyField | prefetch_related | Separate query, Python join | Accessing a set of related objects |
Using prefetch_related on a ForeignKey works, but it runs an extra query instead of using a JOIN. In most cases, select_related is the better choice for a forward relation. Using select_related on a reverse relation raises an error because Django cannot express that relation as a forward lookup.
# Works, but usually less efficient than select_related for a ForeignKey Book.objects.prefetch_related('author') # Raises an error: select_related only supports forward relations Author.objects.select_related('books')
Use select_related when you need the related object itself and the relationship is forward. Use prefetch_related when you need a collection of related objects, or when the relationship is reverse or many-to-many.
Performance and Memory Considerations
The primary benefit of both methods is reducing the number of database round trips. A single query with a JOIN can be faster than dozens of separate queries, but it can also transfer more data because the result set includes columns from multiple tables. prefetch_related avoids large JOIN result sets by keeping queries separate, but it still loads all related objects into memory.
For large datasets, keep these points in mind:
select_relatedwith many joins can produce a very wide result set, increasing memory usage and network transfer.prefetch_relatedloads all related objects for the initial queryset, which can be large if you fetch many parent objects.- Both methods only help if you actually access the related objects. If you do not, you are wasting database work and memory.
Django does not cache related objects across different querysets. If you use select_related or prefetch_related on one queryset, it only affects that queryset. Reusing the same related objects in another queryset will trigger new queries unless you explicitly load them again.
Advanced Usage: Customizing Prefetch Queries
The Prefetch object lets you customize the prefetch query, such as filtering or ordering the related objects. This is useful when you only need a subset of related objects.
from django.db.models import Prefetch published_books = Prefetch('books', queryset=Book.objects.filter(is_published=True)) authors = Author.objects.prefetch_related(published_books)
Now author.books.all() contains only published books, and the filtering happens in the database, not in Python. You can also use to_attr to store the result under a different attribute:
prefetch = Prefetch('books', queryset=Book.objects.order_by('-title'), to_attr='recent_books') authors = Author.objects.prefetch_related(prefetch) for author in authors: print(author.recent_books)
This keeps the original books manager untouched and adds a custom attribute. Use Prefetch when you need to filter, annotate, or order related objects without affecting the original queryset.
Common Pitfalls and Edge Cases
Using select_related on Reverse Relations
select_related only works on forward relations. Attempting to use it on a reverse relation raises FieldError. Use prefetch_related instead.
Prefetching Across Multiple Levels
Nested prefetching works, but each level adds a separate query. For deep hierarchies, the number of queries grows with the depth. Evaluate whether you actually need all levels.
When Related Objects Are Not Accessed
If you call select_related or prefetch_related but never access the related objects, you waste database resources. The ORM cannot know in advance which attributes you will use, so it loads everything you ask for. Be selective.
Database Backend Differences
select_related uses SQL JOINs, which are standard across major databases. prefetch_related uses WHERE ... IN (...) queries, which may have limits on the number of parameters on some databases. For very large querysets, split the work into smaller querysets, for example by ID ranges. Do not call iterator() on a queryset with prefetch_related; it is not supported and can raise an error.
Caching and QuerySet Reuse
When a queryset is evaluated, Django caches the resulting model instances. Reusing the same queryset returns the same cached instances instead of rerunning the SQL query. If the database changes after the queryset is evaluated, create a new queryset to see updated data. Prefetched related objects are cached in the same way and are not shared with other querysets.
Practical Decision Guide
When writing a Django view or management command, follow this reasoning:
- Identify the relationships you access in the loop.
- If it is a forward relation (
ForeignKey,OneToOneField) and you need the single related object, useselect_related. - If it is a reverse relation or
ManyToManyFieldand you need a collection, useprefetch_related. - If you need to filter the related collection, wrap the queryset in a
Prefetchobject. - Measure the number of queries using Django's
connection.queriesor a tool likedjango-debug-toolbarbefore and after optimization.
Applying these methods correctly turns a slow N+1 pattern into a predictable, small number of queries, which is important for keeping Django applications responsive as data grows.