Back to Blog
Python

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.

DjangoORMN+1Query Optimizationselect_relatedprefetch_related
Illustration of Django ORM query optimization showing select_related and prefetch_related reducing database queries.

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 TypeMethodSQL BehaviorBest For
ForeignKeyselect_relatedJOIN, single queryAccessing the related object
OneToOneFieldselect_relatedJOIN, single queryAccessing the related object
Reverse ForeignKeyprefetch_relatedSeparate query, Python joinAccessing a set of related objects
ManyToManyFieldprefetch_relatedSeparate query, Python joinAccessing 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_related with many joins can produce a very wide result set, increasing memory usage and network transfer.
  • prefetch_related loads 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:

  1. Identify the relationships you access in the loop.
  2. If it is a forward relation (ForeignKey, OneToOneField) and you need the single related object, use select_related.
  3. If it is a reverse relation or ManyToManyField and you need a collection, use prefetch_related.
  4. If you need to filter the related collection, wrap the queryset in a Prefetch object.
  5. Measure the number of queries using Django's connection.queries or a tool like django-debug-toolbar before 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.

How to Fix N+1 Queries in Django with select_related and prefetch_related | RYUSLOG DEV