SQL JOIN 으로 한 번의 쿼리로 관련 객체까지 가져옴
# models.py
class User(models.Model):
username = models.CharField(max_length=100)
class UserBlock(models.Model):
blocker = models.ForeignKey(User, related_name='blocking', on_delete=models.CASCADE)
blocked_user = models.ForeignKey(User, related_name='blocked', on_delete=models.CASCADE)
blocked = UserBlock.objects.filter(blocker=user)
for b in blocked:
print(b.blocked_user.nickname) # ← 여기가 매번 추가 쿼리를 발생시킴 (N+1 문제)
→ 위 코드에서 blocked_user.nickname을 조회할 때마다
별도의 쿼리가 실행됨
→ 유저 10명을 차단한 경우,
총 1 (UserBlock 쿼리) + 10 (blocked_user 조회) = 11개의 쿼리
blocked = UserBlock.objects.filter(blocker=user).select_related('blocked_user')
for b in blocked:
print(b.blocked_user.nickname) # ← 이미 join된 상태, 추가 쿼리 없음
→ 이 경우는 SQL 내부에서 JOIN으로 묶어서 단 한 번의 쿼리로 모두 가져옴
두 번의 쿼리로 관련 객체를 가져오고, 파이썬이 메모리에서 매핑
# models.py
class Author(models.Model):
name = models.CharField(max_length=100)
class Book(models.Model):
title = models.CharField(max_length=100)
authors = models.ManyToManyField(Author)
books = Book.objects.all() # 쿼리 1번
for book in books:
for author in book.authors.all(): # ❗ 각 book마다 추가 쿼리
print(f"{book.title} by {author.name}")
books = Book.objects.prefetch_related("authors") # 쿼리 2번 (Book + Author)
for book in books:
for author in book.authors.all(): # ✅ 이미 메모리에 로드됨, 추가 쿼리 없음
print(f"{book.title} by {author.name}")
SELECT FROM book
SELECT FROM author WHERE id IN (...)
쿼리는 2번
반복 접근해도 추가 쿼리 없음
대규모 데이터일수록 성능 차이 큼

