Understanding select_related vs prefetch_related in Django



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 and OneToOneField.

  • 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 reverse ForeignKey.

  • 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