Django ORM: maksimum performans için sorgu optimizasyonu
Django ORM sorgularını optimize etmek için kapsamlı rehber. select_related, prefetch_related, indeksler, N+1 sorunu analizi ve yüksek performanslı uygulamalar için ileri teknikler.

Django ORM, veritabanıyla etkileşim için zarif bir soyutlama sunar; ancak bu sadelik kritik performans sorunlarını gizleyebilir. Kötü optimize edilmiş bir Django uygulaması, tek bir sorgunun yeterli olabileceği durumlarda yüzlerce sorgu üretebilir. Bu rehber, söz konusu sorunları tespit etmek ve çözmek için temel teknikleri ele alır.
Optimize etmeden önce ölçmek gerekir. Geliştirme ortamında django-debug-toolbar kullanmak, üretilen her SQL sorgusunu görmeyi ve darboğazları hızla saptamayı sağlar.
N+1 sorununu anlamak
N+1 sorunu, ORM dünyasının en yaygın tuzağını temsil eder. İlk sorgu N nesne getirdiğinde ve ardından her nesnenin ilişkilerine erişmek için N ek sorgu çalıştırıldığında ortaya çıkar. Bu sorgu çoğalması performansı ciddi biçimde düşürür.
# models.py
from django.db import models
class Author(models.Model):
"""Bir kitabın yazarını temsil eden model."""
name = models.CharField(max_length=200)
email = models.EmailField(unique=True)
bio = models.TextField(blank=True)
def __str__(self):
return self.name
class Book(models.Model):
"""Yazarıyla birlikte bir kitabı temsil eden model."""
title = models.CharField(max_length=300)
author = models.ForeignKey(
Author,
on_delete=models.CASCADE,
related_name='books'
)
published_date = models.DateField()
isbn = models.CharField(max_length=13, unique=True)
def __str__(self):
return self.titleBu sade modeller, N+1 sorununu somut biçimde göstermeye yardımcı olur.
# views.py - Sorunlu örnek
def list_books_bad(request):
"""❌ Bu görünüm N+1 sorgu üretir."""
books = Book.objects.all() # Kitaplar için 1 sorgu
for book in books:
# book.author'a her erişim bir SQL sorgusu tetikler
print(f"{book.title} - {book.author.name}")
# 100 kitap = 101 SQL sorgusu!
return render(request, 'books/list.html', {'books': books})Kod masum görünür ancak her kitap için ilgili yazarı getiren bir sorgu üretir.
select_related ile N+1 sorununu çözmek
select_related metodu bir SQL JOIN gerçekleştirir ve ilişkili verileri tek sorguda getirir. ForeignKey ve OneToOneField ilişkilerinde çalışır.
# views.py - select_related ile optimize çözüm
def list_books_optimized(request):
"""✅ Bu görünüm JOIN içeren tek bir sorgu üretir."""
# select_related bir SQL JOIN yapar
books = Book.objects.select_related('author').all()
for book in books:
# Ek sorgu yok: author zaten yüklü
print(f"{book.title} - {book.author.name}")
# Toplam: kitap sayısından bağımsız 1 SQL sorgusu
return render(request, 'books/list.html', {'books': books})Üretilen SQL sorgusu, yazarları kitaplarla birlikte almak için LEFT OUTER JOIN kullanır.
# İç içe ilişkiler için select_related zincirleme kullanımı
# models.py
class Publisher(models.Model):
name = models.CharField(max_length=200)
country = models.CharField(max_length=100)
class Book(models.Model):
title = models.CharField(max_length=300)
author = models.ForeignKey(Author, on_delete=models.CASCADE)
publisher = models.ForeignKey(Publisher, on_delete=models.CASCADE)
# views.py
def list_books_with_details(request):
"""Kitap, yazar ve yayıncıyı tek sorguda getirir."""
books = Book.objects.select_related(
'author', # Author'a ForeignKey
'publisher' # Publisher'a ForeignKey
).all()
return render(request, 'books/list.html', {'books': books})Birden fazla ilişki, select_related içinde sıralanarak aynı anda optimize edilebilir.
prefetch_related ile ManyToMany ilişkilerini optimize etmek
ManyToMany ilişkileri veya tersine ilişkiler (karşı taraftan ForeignKey) için prefetch_related, devasa join'lerden kaçınarak ayrı ama optimize sorgular çalıştırır.
# models.py
class Tag(models.Model):
"""Kitapları kategorize etmek için etiketler."""
name = models.CharField(max_length=50, unique=True)
def __str__(self):
return self.name
class Book(models.Model):
title = models.CharField(max_length=300)
author = models.ForeignKey(Author, on_delete=models.CASCADE)
tags = models.ManyToManyField(Tag, related_name='books')Book ve Tag arasındaki ManyToMany ilişkisi, etkili optimizasyon için prefetch_related gerektirir.
# views.py - ManyToMany optimizasyonu
def list_books_with_tags(request):
"""✅ Kitapları ve etiketlerini verimli biçimde getirir."""
books = Book.objects.prefetch_related('tags').all()
for book in books:
# Etiketler önceden yüklendi, ek sorgu yok
tag_names = [tag.name for tag in book.tags.all()]
print(f"{book.title}: {', '.join(tag_names)}")
# Toplam: 2 sorgu (kitaplar + etiketler), miktarlar fark etmeksizin
return render(request, 'books/list.html', {'books': books})prefetch_related etiketler için ayrı bir sorgu çalıştırır, ardından birleştirmeyi Python tarafında yapar.
ForeignKey ve OneToOne için select_related kullanılmalıdır (SQL JOIN). ManyToMany ve tersine ilişkiler için prefetch_related tercih edilir (ayrı sorgular). İkisi aynı QuerySet üzerinde birleştirilebilir.
Prefetch nesneleriyle ön yüklemeyi özelleştirmek
Prefetch nesnesi, önceden yüklenen veriler üzerinde ince denetim sağlar: filtreleme, sıralama ve hatta sonuç sayısını sınırlandırma.
# views.py
from django.db.models import Prefetch
def list_authors_with_recent_books(request):
"""Yazarları yalnızca güncel kitaplarıyla getirir."""
# Özel Prefetch: yalnızca 2025'ten itibaren çıkan kitaplar
recent_books_prefetch = Prefetch(
'books',
queryset=Book.objects.filter(
published_date__year__gte=2025
).order_by('-published_date'),
to_attr='recent_books' # Özel bir nitelikte saklanır
)
authors = Author.objects.prefetch_related(
recent_books_prefetch
).all()
for author in authors:
# Özel nitelik üzerinden erişim
for book in author.recent_books:
print(f"{author.name}: {book.title}")
return render(request, 'authors/list.html', {'authors': authors})to_attr niteliği sonuçları her zamanki manager yerine bir Python listesinde tutar.
# Gelişmiş kombinasyon: select_related + Prefetch
def list_authors_complete(request):
"""Çok seviyeli optimizasyona dair kapsamlı örnek."""
authors = Author.objects.prefetch_related(
Prefetch(
'books',
queryset=Book.objects.select_related(
'publisher' # Her kitabın yayıncısını da optimize eder
).prefetch_related(
'tags' # Ve her kitabın etiketlerini
).filter(published_date__year=2026)
)
).all()
return render(request, 'authors/complete.html', {'authors': authors})Bu yaklaşım, karmaşık veri yapılarında sorgu sayısını çarpıcı biçimde düşürür.
Django mülakatlarında başarılı olmaya hazır mısın?
İnteraktif simülatörler, flashcards ve teknik testlerle pratik yap.
only() ve defer() ile sütun sınırlamak
Django varsayılan olarak tablonun tüm sütunlarını getirir. Çok alanlı veya büyük alanlı modellerde sütunları sınırlamak performansı iyileştirir.
# views.py
def list_books_minimal(request):
"""Yalnızca gerekli sütunları getirir."""
# only() dahil edilecek sütunları belirler
books = Book.objects.only(
'id',
'title',
'published_date'
).select_related('author')
# Dikkat: dahil edilmeyen bir alana erişim ek sorgu tetikler
for book in books:
print(book.title) # OK, dahil
# print(book.isbn) # Ek bir sorguya yol açardı
return render(request, 'books/list.html', {'books': books})only() metodu yalnızca belirtilen sütunları yükleyen "deferred" bir nesne oluşturur.
# defer() ile belirli sütunları dışlamak
def list_authors_without_bio(request):
"""Nadiren kullanılan büyük alanları dışlar."""
# defer() belirtilen sütunları dışlar
authors = Author.objects.defer(
'bio' # TextField yüklenmez
).all()
for author in authors:
print(author.name) # OK
print(author.email) # OK
# author.bio talep üzerine yüklenir
return render(request, 'authors/list.html', {'authors': authors})defer() yöntemi only()'in tersi gibi çalışır: listelenen sütunlar başlangıçta yüklenmez.
values() ve values_list() ile optimizasyon
Tam model nesneleri yerine yalnızca belirli değerler gerektiğinde, values() ve values_list() daha hafif sözlükler veya tuple'lar döndürür.
# views.py
def get_book_titles(request):
"""Yalnızca başlıkları liste olarak getirir."""
# values_list tuple döndürür
titles = Book.objects.values_list('title', flat=True)
# Sonuç: ['Kitap 1', 'Kitap 2', ...]
# values sözlük döndürür
book_data = Book.objects.values('title', 'published_date')
# Sonuç: [{'title': 'Kitap 1', 'published_date': ...}, ...]
return render(request, 'books/titles.html', {'titles': titles})Bu yöntemler model nesnelerinin oluşturulmasını engelleyerek bellek tüketimini azaltır.
# Toplulaştırmalarla kombinasyon
from django.db.models import Count, Avg
def get_author_statistics(request):
"""Nesneleri yüklemeden yazara göre istatistikler."""
stats = Author.objects.values('name').annotate(
book_count=Count('books'),
avg_year=Avg('books__published_date__year')
).order_by('-book_count')
# Sonuç: [{'name': 'Yazar', 'book_count': 5, 'avg_year': 2024}, ...]
return render(request, 'authors/stats.html', {'stats': stats})Annotation ile hesaplamalar doğrudan veritabanında yapılabilir.
Sorguları hızlandırmak için indeks oluşturmak
Veritabanı indeksleri aramaları çarpıcı biçimde hızlandırır. Django bunları doğrudan modellerde tanımlamaya olanak tanır.
# models.py
from django.db import models
class Book(models.Model):
title = models.CharField(max_length=300, db_index=True)
author = models.ForeignKey(Author, on_delete=models.CASCADE)
published_date = models.DateField()
isbn = models.CharField(max_length=13, unique=True)
status = models.CharField(max_length=20, default='available')
class Meta:
# Sık sorgular için bileşik indeksler
indexes = [
# Yayın tarihi indeksi (sık sıralama)
models.Index(
fields=['published_date'],
name='book_pub_date_idx'
),
# Yazar + durum filtresi için bileşik indeks
models.Index(
fields=['author', 'status'],
name='book_author_status_idx'
),
# Kısmi indeks: yalnızca müsait kitaplar
models.Index(
fields=['published_date'],
name='book_available_idx',
condition=models.Q(status='available')
),
]
# İndeksten faydalanan varsayılan sıralama
ordering = ['-published_date']Bu indeksler, ilgili sütunlarda filtreleme yapan sorguların performansını iyileştirir.
İndeksler okumayı hızlandırır, ancak yazma işlemlerini (INSERT, UPDATE) hafifçe yavaşlatır. Yalnızca WHERE, ORDER BY veya JOIN içinde sıkça kullanılan sütunlarda indeks oluşturmak gerekir.
Gerektiğinde ham SQL sorgularına başvurmak
Karmaşık sorgular veya veritabanına özgü optimizasyonlar için ham SQL sorguları tam denetim sağlar.
# views.py
from django.db import connection
def get_books_with_raw_sql(request):
"""Özel durumlar için ham sorgu."""
# 1. yöntem: model nesnesi döndürmek için raw()
books = Book.objects.raw('''
SELECT b.*, a.name as author_name
FROM library_book b
INNER JOIN library_author a ON b.author_id = a.id
WHERE b.published_date > %s
ORDER BY b.published_date DESC
''', ['2025-01-01'])
return render(request, 'books/list.html', {'books': books})
def execute_custom_query(request):
"""SELECT olmayan sorgular için doğrudan yürütme."""
with connection.cursor() as cursor:
# Karmaşık toplulaştırmalı sorgu
cursor.execute('''
SELECT
a.name,
COUNT(b.id) as book_count,
AVG(EXTRACT(YEAR FROM b.published_date)) as avg_year
FROM library_author a
LEFT JOIN library_book b ON b.author_id = a.id
GROUP BY a.id, a.name
HAVING COUNT(b.id) > 2
ORDER BY book_count DESC
''')
results = cursor.fetchall()
return render(request, 'stats.html', {'results': results})Ham sorgular ORM'i devre dışı bırakır ancak veritabanı bağımsızlığını kaybettirir.
django-debug-toolbar ile sorgu analizi
django-debug-toolbar aracı, bir Django görünümünün ürettiği tüm SQL sorgularını görselleştirir.
# settings.py - Debug toolbar yapılandırması
INSTALLED_APPS = [
# ... diğer uygulamalar
'debug_toolbar',
]
MIDDLEWARE = [
'debug_toolbar.middleware.DebugToolbarMiddleware',
# ... diğer middleware'ler
]
# Yerel istekler için toolbar göster
INTERNAL_IPS = ['127.0.0.1']
DEBUG_TOOLBAR_PANELS = [
'debug_toolbar.panels.sql.SQLPanel', # SQL sorguları
'debug_toolbar.panels.timer.TimerPanel', # Yürütme süresi
'debug_toolbar.panels.cache.CachePanel', # Cache
]Bu yapılandırma optimizasyon için en yararlı panelleri etkinleştirir.
# Geliştirme ortamında SQL sorgu loglama
# settings.py
LOGGING = {
'version': 1,
'handlers': {
'console': {
'class': 'logging.StreamHandler',
},
},
'loggers': {
'django.db.backends': {
'level': 'DEBUG',
'handlers': ['console'],
},
},
}Bu loglama her SQL sorgusunu konsolda gösterir; N+1 sorunlarını tespit etmek için faydalıdır.
QuerySet'leri önbelleğe almak
Sık erişilen ve nadiren değişen veriler için önbellek, tekrarlanan sorguları önler.
# views.py
from django.core.cache import cache
from django.views.decorators.cache import cache_page
def list_featured_books(request):
"""Öne çıkan kitapları önbellekle getirir."""
cache_key = 'featured_books_list'
# Önbellekten okuma denemesi
books = cache.get(cache_key)
if books is None:
# Cache miss: sorguyu çalıştır
books = list(
Book.objects.select_related('author')
.filter(featured=True)
.order_by('-published_date')[:10]
)
# 5 dakika önbelleğe al
cache.set(cache_key, books, timeout=300)
return render(request, 'books/featured.html', {'books': books})
# Tüm görünümü önbelleğe alan dekoratör
@cache_page(60 * 15) # 15 dakika cache
def list_all_tags(request):
"""Tüm etiketleri listeler (nadiren değişen veri)."""
tags = Tag.objects.annotate(
book_count=Count('books')
).order_by('-book_count')
return render(request, 'tags/list.html', {'tags': tags})Önbellek, statik veriler için veritabanı yükünü azaltır.
Pratik yapmaya başla!
Mülakat simülatörleri ve teknik testlerle bilgini test et.
Sonuç
Django ORM sorgu optimizasyonu birkaç temel ilkeye dayanır:
✅ Optimize etmeden önce ölçmek: gerçek sorunları tespit etmek için django-debug-toolbar kullanmak
✅ N+1 sorununu ortadan kaldırmak: ForeignKey için select_related, ManyToMany için prefetch_related
✅ Veriyi sınırlamak: yalnızca gerekli olanı yüklemek için only(), defer(), values()
✅ Akıllı indekslemek: sık filtrelenen veya sıralanan sütunlarda indeks oluşturmak
✅ Stratejik önbellek: nadiren değişen veriler Django önbelleğinden faydalanır
✅ Ham sorgular: yalnızca veritabanına özgü optimizasyonlar için son çare
Metodik biçimde uygulandığında bu teknikler, yavaş bir Django uygulamasını büyük veri hacimlerini idare edebilen verimli bir sisteme dönüştürür. Django ORM, incelikleri kavrandığında güçlü bir araç olmayı sürdürür.
Pratik yapmaya başla!
Mülakat simülatörleri ve teknik testlerle bilgini test et.
Etiketler
Paylaş
İlgili makaleler

Django 5.2 Middleware ve Signal Mekanizmalari: Teknik Mulakat Rehberi
Django 5.2'de custom middleware yazimi, signal handling, post_save, pre_save, async middleware ve domain olaylari. Teknik mulakat sorularina yonelik kapsamli rehber ve Python kod ornekleri.

Django ve Python Mulakat Sorulari: 2026'nin En Onemli 25 Sorusu
Django ve Python mulakatlarinda en sik sorulan 25 soru. ORM, gorunumler, middleware, DRF, sinyaller ve optimizasyon detayli cevaplar ve kod ornekleriyle.

Django 5: Django REST Framework ile REST API Olusturma
Django 5 ve DRF ile profesyonel REST API olusturma rehberi. Serializer'lar, ViewSet'ler, JWT kimlik dogrulama ve en iyi uygulamalar.