select_related و prefetch_related (Performance)
بهینهسازی Query: جلوگیری از N+1 Problem ⚡
وقتی از ForeignKey یا ManyToMany استفاده میکنید، ممکنه مشکل N+1 Query پیش بیاد. یعنی برای هر شیء یک Query جداگانه اجرا بشه. select_related و prefetch_related این مشکل رو حل میکنن!
مشکل N+1
# ❌ بد - N+1 Query Problem
posts = Post.objects.all()
for post in posts:
print(post.author.username) # برای هر پست یک Query جدید!
# ✅ خوب - فقط 2 Query
posts = Post.objects.select_related('author')
for post in posts:
print(post.author.username) # همه دادهها از قبل لود شدن!
select_related: برای ForeignKey
select_related برای روابط ForeignKey و OneToOne استفاده میشه. با یک JOIN در SQL همه دادهها رو یکجا میگیره:
from blog.models import Post
# لود کردن author برای همه پستها در یک Query
posts = Post.objects.select_related('author')
# برای چند ForeignKey
posts = Post.objects.select_related('author', 'category')
# استفاده در فیلتر
posts = Post.objects.filter(published=True).select_related('author')
prefetch_related: برای ManyToMany و Reverse ForeignKey
prefetch_related برای روابط ManyToMany و Reverse ForeignKey استفاده میشه:
from blog.models import Post, Tag
# لود کردن تگهای همه پستها
posts = Post.objects.prefetch_related('tags')
# لود کردن کامنتهای همه پستها
posts = Post.objects.prefetch_related('comment_set')
# ترکیب با select_related
posts = Post.objects.select_related('author').prefetch_related('tags', 'comment_set')
قانون طلایی:
- ✅
select_relatedبرای ForeignKey و OneToOne - ✅
prefetch_relatedبرای ManyToMany و Reverse ForeignKey - ✅ همیشه وقتی از relation استفاده میکنید، اینها رو اضافه کنید!
مثال عملی
# blog/views.py
def post_list(request):
# بهینه شده - فقط 3 Query به جای 100+ Query!
posts = Post.objects.select_related(
'author', 'category'
).prefetch_related(
'tags', 'comment_set'
).filter(published=True)
return render(request, 'blog/list.html', {'posts': posts})
آماده رفتن به درس بعدی هستید؟
این درس را به پایان رساندید و میتوانید به درس بعدی بروید.