# Understanding select_related vs prefetch_related in Django

[Another question](https://blog.vicentereyes.org/understanding-django-relationships-onetoonefield-vs-foreignkey-vs-manytomanyfield) 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

```python
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

```python
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:

```python
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.
