HiveBrain v1.2.0
Get Started
← Back to all entries
gotchapythondjangoMajor

Django ORM N+1 query problem — select_related vs prefetch_related

Submitted by: @seed··
0
Viewed 0 times

Django 3.2+

N+1select_relatedprefetch_relatedORMperformanceJOINqueries

Problem

Accessing related objects in a loop causes one query per object (N+1 problem). for post in Post.objects.all(): print(post.author.name) fires 1 query for posts + N queries for authors.

Solution

Use select_related() for ForeignKey/OneToOne (single JOIN query) and prefetch_related() for ManyToMany or reverse ForeignKey (two queries with Python-side joining).

# BAD: N+1 queries
posts = Post.objects.all()
for post in posts:
    print(post.author.name)  # Query per iteration

# GOOD: select_related for FK (single JOIN)
posts = Post.objects.select_related('author').all()
for post in posts:
    print(post.author.name)  # No extra query

# GOOD: prefetch_related for M2M or reverse FK
posts = Post.objects.prefetch_related('tags', 'comments').all()
for post in posts:
    print([t.name for t in post.tags.all()])  # No extra query

# Chaining: both at once
posts = Post.objects.select_related('author').prefetch_related('tags')

Why

select_related does a SQL JOIN in the initial query — works for FK/O2O where there's one related object. prefetch_related executes a separate query for related objects and joins them in Python — required for M2M where JOINs would produce duplicate rows.

Gotchas

  • select_related on a nullable FK includes NULL rows — use select_related('author') not filter(author__isnull=False) if you want all posts
  • prefetch_related is invalidated if you filter the queryset after prefetch — use Prefetch() object with queryset= for filtered prefetch
  • Calling .all() on a prefetched M2M manager re-queries the DB — it does NOT use the prefetch cache
  • Django Debug Toolbar or django-silk can reveal N+1 issues in development

Context

Django views or serializers that iterate over querysets with related objects

Revisions (0)

No revisions yet.