Fix N+1 with select_related (JOIN) and prefetch_related (separate query); annotate for counts.
Optimizing Queries
# N+1 problem — BAD
for post in Post.objects.all():
print(post.author.name) # 1 query per post!
# Fix with select_related (SQL JOIN — FK/OneToOne)
posts = Post.objects.select_related("author").all()
# prefetch_related (separate query — M2M/reverse FK)
posts = Post.objects.prefetch_related("tags", "comments__user")
# Annotate instead of property
from django.db.models import Count
posts = Post.objects.annotate(comment_count=Count("comments"))
# Check query count in tests
with self.assertNumQueries(2):
posts = list(Post.objects.select_related("author"))