← Engineering IssuesMN2RB / CASE FILES

Understanding the N+1 Problem in Django (And How to Fix It)

One query for the list, N more for the relations. How the N+1 problem sneaks into Django and how select_related and prefetch_related stop it.

Read the original on Medium ↗
FIELD NOTE

If you’re working with Django and relational databases, chances are you’ve encountered performance issues caused by the infamous ‘N+1 query problem’. This blog will explain what it is, why it’s a problem, and how you can fix it with Django’s query optimization tools.

The N+1 query problem.
The N+1 query problem.

The N+1 query problem occurs when Django runs one query to fetch initial data (the “1”) and then N additional queries to fetch related data for each item, leading to poor performance.

§1Let’s dive into it

With models like:

class User(models.Model):
    name = models.CharField(max_length=100)

class Order(models.Model):
    user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='orders')
    product = models.CharField(max_length=100)

§2The Problem

The N+1 query problem typically appears in two scenarios:

§31. Accessing Related Objects in a Loop (One-to-Many)

When you fetch a queryset and then access related objects (like ForeignKey or reverse relationships), Django performs one query for the initial data and one additional query for each related object.

orders = Order.objects.all()  # Get all orders from the database.
for order in orders:          # For each order...
    print(order.user.name)
  • 1 query for the orders.
  • N queries for each order’s related user.

§42. Accessing Reverse Relationships with Multiple Values (Many-to-Many / Reverse ForeignKey)

When you need to access related data in reverse (like ManyToManyField or reverse ForeignKey), Django fetches related objects in a separate query for each parent object.

users = User.objects.all()  # 1 query to fetch all users.

for user in users:  
    orders = user.orders.all()  # N queries to fetch orders for each user.
  • 1 query for the users.
  • N queries for each user’s related orders.

§5The Impact of the N+1 Query Problem on Your Django App

  • Increased Database Load: More queries consume more resources, slowing down your app.
  • Slower Response Time: More queries = higher latency, resulting in a slower user experience.
  • Wasted Database Connections: Multiple queries increase connections, which can cause issues.
  • Higher Latency with Large Datasets: As data grows, the problem worsens, causing performance bottlenecks.
  • Poor Scalability: As your app scales, the N+1 problem slows down performance even more.

§6Solution

§7Solution 1: select_related

select_related is used to fetch related objects in a single query for ForeignKey and OneToOneField relationships by using SQL joins.

orders = Order.objects.select_related('user').all()  # 1 query to fetch all orders and their related users.

for order in orders:
    print(order.user.name)

1 query to fetch both Order and User data

§8Solution 2: prefetch_related

prefetch_related is used for reverse relationships, where you want to fetch related objects in a separate query and link them in Python.

users = User.objects.prefetch_related('orders').all()  # 1 query to fetch users + 1 query to fetch all orders.

for user in users:
    orders=user.orders.all():

2 queries — one for the users and one for their orders.

§9Conclusion: Solving the N+1 Query Problem in Django

The N+1 query problem occurs when your application executes 1 query to fetch the main data and N additional queries to fetch related data for each item. This can lead to inefficiencies, particularly as the dataset grows.

§10How to Solve It

  • select_related: Use for ForeignKey and OneToOneField relationships. This method fetches both the main object and related objects in a single query by using SQL joins, reducing the number of queries.
  • prefetch_related: Use for reverse relationships or ManyToMany relationships. It executes separate queries to fetch the main data and related objects, but efficiently links them in Python.

By applying select_related and prefetch_related, you can minimize the number of queries and improve the performance of your Django app.

§11Best Practice

  • Optimize queries by using select_related for single reference relationships and prefetch_related for collections of related objects. This approach ensures efficient data retrieval and better scalability as your app grows.

More case studies

2025-09 · 5 min · When Monoliths Break: Rethinking Transactions in MicroservicesRead →2025-02 · 2 min · Tracking Django Schema Changes with django-schema-historyRead →