# 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. - Published: 2026-02-15 - Updated: 2026-04-28 - Author: SharpSkill - Tags: django, django orm, python, veritabanı optimizasyonu, performans - Reading time: 14 min --- 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. > **Optimizasyonun altın kuralı** > > 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. ```python # 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.title ``` Bu sade modeller, N+1 sorununu somut biçimde göstermeye yardımcı olur. ```python # 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. ```python # 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. ```python # İç 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. ```python # 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. ```python # 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. > **select_related vs prefetch_related** > > 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. ```python # 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. ```python # 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. ## 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. ```python # 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. ```python # 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. ```python # 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. ```python # 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. ```python # 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 ve yazma işlemleri** > > İ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. ```python # 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. ```python # 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. ```python # 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. ```python # 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. ## 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. --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/tr/blog/django/django-orm-optimizing-queries