This content originally appeared on DEV Community and was authored by Vicente G. Reyes
Another question I was asked in my technical interview was the difference between select_related
and prefetch_related
. I’m going to talk about it in this article so we can all learn from it,
When working with Django’s ORM, one of the most common performance issues developers run into is the N+1 query problem. This happens when each object in a queryset triggers its own database query to fetch related data. Thankfully, Django gives us two powerful tools to optimize queries: select_related
and prefetch_related
.
Both methods help reduce database hits, but they work differently under the hood. Let’s break it down.
What is select_related
?
How it works
Used for single-valued relationships:
ForeignKey
andOneToOneField
.Performs an SQL JOIN to fetch related objects in a single query.
Best when you know you’ll need related data for each object.
Example
class Author(models.Model):
name = models.CharField(max_length=100)
class Book(models.Model):
title = models.CharField(max_length=100)
author = models.ForeignKey(Author, on_delete=models.CASCADE)
# Without select_related
books = Book.objects.all()
for book in books:
print(book.author.name) # Each loop triggers a new query!
# With select_related
books = Book.objects.select_related("author")
for book in books:
print(book.author.name) # Single JOIN query!
Result: Faster queries with less database load when dealing with one-to-one or many-to-one relationships.
What is prefetch_related?
How it works
Used for multi-valued relationships:
ManyToManyField
and reverseForeignKey
.Executes separate queries for related objects, then merges them in Python.
Ideal for cases where multiple related rows need to be fetched.
Example
class Author(models.Model):
name = models.CharField(max_length=100)
class Book(models.Model):
title = models.CharField(max_length=100)
author = models.ForeignKey(Author, related_name="books", on_delete=models.CASCADE)
# Without prefetch_related
authors = Author.objects.all()
for author in authors:
for book in author.books.all():
print(book.title) # Triggers a query for each author!
# With prefetch_related
authors = Author.objects.prefetch_related("books")
for author in authors:
for book in author.books.all():
print(book.title) # Just 2 queries total!
Result: Fewer queries overall when fetching multiple related objects.
Key Differences Between select_related
and prefetch_related
Feature | select_related |
prefetch_related |
---|---|---|
Works with |
ForeignKey , OneToOneField
|
ManyToManyField , reverse ForeignKey
|
Query type | SQL JOIN (single query) | Multiple queries + Python join |
Best for | Single-related objects | Multi-related objects |
Performance impact | Reduces queries for one-to-one/many-to-one lookups | Reduces queries for many-to-many/one-to-many lookups |
Rule of Thumb
Use
select_related
when fetching single-related objects (ForeignKey, OneToOne).Use
prefetch_related
when fetching collections of related objects (ManyToMany, reverse ForeignKey).
By using these tools wisely, you can drastically improve your Django application’s performance and keep your database queries efficient.
Pro Tip: You can even combine them:
Book.objects.select_related("author").prefetch_related("reviews")
This way, you optimize both single-valued and multi-valued relationships in one go!
Final Thoughts
Understanding the difference between select_related
and prefetch_related
is essential for writing efficient Django queries. select_related
leverages SQL joins for single-related lookups, while prefetch_related
handles multi-related lookups by batching queries and joining in Python.
Mastering these techniques will save you from performance bottlenecks and keep your Django apps running smoothly.
This content originally appeared on DEV Community and was authored by Vicente G. Reyes