# Django REST Framework Serializer Derinlemesine İnceleme: Doğrulama, İç İçe Yapılar ve N+1 > Gelişmiş doğrulama teknikleri, iç içe serializer kalıpları ve N+1 sorgu optimizasyon stratejileri ile DRF serializerlarında ustalaşın. Üretime hazır kod örnekleri dahil. - Published: 2026-07-12 - Updated: 2026-07-12 - Author: SharpSkill - Tags: django, drf, serializers, api, performance - Reading time: 11 min --- Django REST Framework serializerları, queryset'leri ve model örneklerini JSON yanıtlarına dönüştürme ve veritabanına ulaşmadan önce gelen verileri doğrulama gibi karmaşık görevleri üstlenir. Temel serializer kullanımı basit görünse de, üretim uygulamaları doğrulama pipeline'larında, iç içe ilişkilerde ve sorgu optimizasyonunda ustalık gerektirir. > **DRF Serializer Performans Kuralı** > > `select_related`/`prefetch_related` olmadan ilişkili nesnelere erişen her `SerializerMethodField` veya iç içe serializer ek veritabanı sorguları tetikler. 3 ilişkisi olan 100 nesnelik bir liste, 4 yerine 301 sorgu anlamına gelir. ## DRF Serializer Doğrulama Pipeline'ını Anlamak DRF serializerları doğrulamayı belirli bir sırada yürütür: alan düzeyinde serileştirme, alan düzeyinde doğrulayıcılar, ardından `validate()` aracılığıyla nesne düzeyinde doğrulama. Bu pipeline, veri dönüşümlerinin ne zaman ve nasıl yakalanacağını belirler. Doğrulama dizisi, ilkel veri türlerini serileştiren ve alan doğrulayıcılarını çalıştıran `to_internal_value()` ile başlar. Tüm alanlar bireysel doğrulamayı geçtikten sonra, çapraz alan kontrolleri için `validate()` çalışır. ```python # serializers.py from rest_framework import serializers from django.utils import timezone from .models import Event class EventSerializer(serializers.ModelSerializer): start_date = serializers.DateTimeField() end_date = serializers.DateTimeField() class Meta: model = Event fields = ['id', 'title', 'start_date', 'end_date', 'location'] def validate_start_date(self, value): # Field-level validation runs first if value < timezone.now(): raise serializers.ValidationError("Start date cannot be in the past.") return value def validate(self, attrs): # Object-level validation runs after all fields validate start = attrs.get('start_date') end = attrs.get('end_date') if start and end and end <= start: raise serializers.ValidationError({ 'end_date': "End date must be after start date." }) return attrs ``` Bu ayrım ayrıntılı kontrol sağlar: açıkça geçersiz alan değerlerini erken yakalayın, ardından birden fazla alanı kapsayan iş kurallarını doğrulayın. ## Özel Doğrulayıcılar ve Yeniden Kullanılabilir Doğrulama Mantığı DRF üç doğrulayıcı kalıbını destekler: alan düzeyinde metodlar, bağımsız doğrulayıcı sınıfları ve `validators` argümanı. Bağımsız doğrulayıcılar, serializerlar arasında yeniden kullanılabilirliği teşvik eder ve tek sorumluluk ilkesini korur. ```python # validators.py from rest_framework import serializers import re class SlugFormatValidator: """Validates URL-safe slug format.""" def __init__(self, allow_unicode=False): self.allow_unicode = allow_unicode self.pattern = r'^[\w-]+$' if allow_unicode else r'^[a-z0-9-]+$' def __call__(self, value): if not re.match(self.pattern, value): raise serializers.ValidationError( "Slug must contain only lowercase letters, numbers, and hyphens." ) class UniqueForUserValidator: """Validates uniqueness scoped to current user.""" requires_context = True def __init__(self, queryset, field): self.queryset = queryset self.field = field def __call__(self, value, serializer_field): request = serializer_field.context.get('request') if not request or not request.user.is_authenticated: return queryset = self.queryset.filter( user=request.user, **{self.field: value} ) # Exclude current instance during updates instance = serializer_field.parent.instance if instance: queryset = queryset.exclude(pk=instance.pk) if queryset.exists(): raise serializers.ValidationError( f"You already have an item with this {self.field}." ) ``` Doğrulayıcıları bildirimsel olarak uygulamak, serializer sınıflarının doğrulama uygulaması yerine yapıya odaklanmasını sağlar. ```python # serializers.py from .validators import SlugFormatValidator, UniqueForUserValidator from .models import Project class ProjectSerializer(serializers.ModelSerializer): slug = serializers.CharField( max_length=100, validators=[ SlugFormatValidator(), UniqueForUserValidator(Project.objects.all(), 'slug') ] ) class Meta: model = Project fields = ['id', 'name', 'slug', 'description'] ``` ## İç İçe Serializerlar: Yazılabilir İlişkileri Doğru Şekilde Uygulamak İç içe serializerlar, tek bir istekte ilişkili nesnelerin okunmasını ve yazılmasını sağlar. Zorluk, oluşturma, güncelleme işlemlerini ve ilişkiler arasında referans bütünlüğünü korumakta yatar. Okuma işlemleri için iç içe serializerlar otomatik olarak çalışır. Yazma işlemleri, DRF'nin iç içe verileri nasıl işleyeceğini çıkaramadığı için açık `create()` ve `update()` metod geçersiz kılmaları gerektirir. ```python # models.py from django.db import models class Author(models.Model): name = models.CharField(max_length=200) email = models.EmailField(unique=True) class Book(models.Model): title = models.CharField(max_length=300) isbn = models.CharField(max_length=13, unique=True) author = models.ForeignKey(Author, on_delete=models.CASCADE, related_name='books') class Chapter(models.Model): book = models.ForeignKey(Book, on_delete=models.CASCADE, related_name='chapters') number = models.PositiveIntegerField() title = models.CharField(max_length=200) ``` Serializer, bir kitap içinde iç içe bölüm oluşturma ve güncellemelerini yönetir: ```python # serializers.py from rest_framework import serializers from django.db import transaction from .models import Author, Book, Chapter class ChapterSerializer(serializers.ModelSerializer): id = serializers.IntegerField(required=False) # Allow ID for updates class Meta: model = Chapter fields = ['id', 'number', 'title'] class BookSerializer(serializers.ModelSerializer): chapters = ChapterSerializer(many=True) author_name = serializers.CharField(source='author.name', read_only=True) class Meta: model = Book fields = ['id', 'title', 'isbn', 'author', 'author_name', 'chapters'] @transaction.atomic def create(self, validated_data): chapters_data = validated_data.pop('chapters', []) book = Book.objects.create(**validated_data) Chapter.objects.bulk_create([ Chapter(book=book, **chapter_data) for chapter_data in chapters_data ]) return book @transaction.atomic def update(self, instance, validated_data): chapters_data = validated_data.pop('chapters', []) # Update book fields for attr, value in validated_data.items(): setattr(instance, attr, value) instance.save() # Track existing chapters for deletion detection existing_ids = set(instance.chapters.values_list('id', flat=True)) updated_ids = set() for chapter_data in chapters_data: chapter_id = chapter_data.pop('id', None) if chapter_id and chapter_id in existing_ids: # Update existing chapter Chapter.objects.filter(id=chapter_id).update(**chapter_data) updated_ids.add(chapter_id) else: # Create new chapter Chapter.objects.create(book=instance, **chapter_data) # Delete chapters not included in request instance.chapters.filter(id__in=existing_ids - updated_ids).delete() return instance ``` `@transaction.atomic` dekoratörü, tüm iç içe işlemlerin birlikte başarılı veya başarısız olmasını sağlar—üretim API'lerinde veri tutarlılığı için kritik önem taşır. > **İç İçe Serializer Güncelleme Tuzağı** > > İç içe serializerlarda açık ID yönetimi olmadan, her güncelleme isteği mevcut olanları değiştirmek yerine yeni ilişkili nesneler oluşturur. Güncellenebilir iç içe nesneler için her zaman `id = serializers.IntegerField(required=False)` ekleyin. ## DRF Serializerlarında N+1 Sorgu Problemini Çözmek N+1 sorguları, ilişkili nesnelerle listeleri serileştirirken oluşur. Listedeki her öğe, ilişkileri için ayrı sorgular tetikler ve API yanıt sürelerini yıkıcı şekilde etkiler. [Django ORM sorgu optimizasyon kalıpları](/blog/django/django-orm-optimizing-queries) DRF'ye doğrudan uygulanır. Yazarları ve bölümleriyle birlikte 50 kitap döndüren bir görünümü düşünün: ```python # views.py - PROBLEMATIC: N+1 queries from rest_framework import generics from .models import Book from .serializers import BookSerializer class BookListView(generics.ListAPIView): queryset = Book.objects.all() # 1 query for books serializer_class = BookSerializer # +50 queries for authors, +50 for chapters = 101 total ``` Çözüm, yabancı anahtarlar için `select_related` ve ters ilişkiler için `prefetch_related` gerektirir: ```python # views.py - OPTIMIZED: 3 queries total class BookListView(generics.ListAPIView): queryset = Book.objects.select_related('author').prefetch_related('chapters') serializer_class = BookSerializer ``` Koşullu mantık içeren karmaşık serializerlar için, serializer gereksinimlerini karşılamak üzere `get_queryset()` metodunu geçersiz kılın: ```python # views.py from rest_framework import viewsets from django.db.models import Prefetch, Count from .models import Author from .serializers import AuthorDetailSerializer class AuthorViewSet(viewsets.ModelViewSet): serializer_class = AuthorDetailSerializer def get_queryset(self): return Author.objects.prefetch_related( Prefetch( 'books', queryset=Book.objects.select_related('publisher').annotate( chapter_count=Count('chapters') ).order_by('-publication_date') ) ) ``` ## SerializerMethodField Performans Optimizasyonu `SerializerMethodField`, serileştirilen her örnek için Python kodu çalıştırır. Bu metodlar içinde veritabanı sorguları yapmak, standart sorgu günlüklerinde görünmeyen gizli N+1 problemleri oluşturur. ```python # serializers.py - PROBLEMATIC class AuthorSerializer(serializers.ModelSerializer): total_sales = serializers.SerializerMethodField() class Meta: model = Author fields = ['id', 'name', 'total_sales'] def get_total_sales(self, obj): # Query executed for EACH author in the list return obj.books.aggregate(total=Sum('sales'))['total'] or 0 ``` Çözüm, toplamayı queryset düzeyine taşır: ```python # views.py from django.db.models import Sum class AuthorListView(generics.ListAPIView): queryset = Author.objects.annotate( total_sales=Sum('books__sales') ) serializer_class = AuthorSerializer ``` ```python # serializers.py - OPTIMIZED class AuthorSerializer(serializers.ModelSerializer): total_sales = serializers.IntegerField(read_only=True) # From annotation class Meta: model = Author fields = ['id', 'name', 'total_sales'] ``` Anotasyonlu alan normal bir serializer alanı haline gelir ve örnek başına sorguları tamamen ortadan kaldırır. ## Serializer Bağlamı ile Dinamik Alan Seçimi Üretim API'leri genellikle alan esnekliğine ihtiyaç duyar—mobil istemciler minimum yük isterken yönetici panelleri tam veri gerektirir. Dinamik serializerlar, istek bağlamına göre çıktıyı uyarlar. ```python # serializers.py class DynamicFieldsMixin: """Allows field selection via ?fields=id,name,email query parameter.""" def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) request = self.context.get('request') if not request: return fields_param = request.query_params.get('fields') if fields_param: requested = set(fields_param.split(',')) existing = set(self.fields.keys()) # Remove fields not in request for field_name in existing - requested: self.fields.pop(field_name) class UserSerializer(DynamicFieldsMixin, serializers.ModelSerializer): class Meta: model = User fields = ['id', 'username', 'email', 'first_name', 'last_name', 'date_joined'] ``` `/api/users/?fields=id,username` istekleri yalnızca bu alanları döndürür, yük boyutunu azaltır ve seçilen alanlara göre daha fazla sorgu optimizasyonuna potansiyel olanak tanır. > **Serializer Bağlamı Erişilebilirliği** > > Bağlam yalnızca serializer bir istekle oluşturulduğunda doldurulur. `UserSerializer(data=payload)` gibi doğrudan oluşturma boş bağlama sahiptir—ViewSet'lerde veya View'larda her zaman `context={'request': request}` geçirin. ## Performans İzleme ve Sorgu Analizi Serializer kaynaklı sorgu problemlerini tanımlamak, veritabanı işlemlerine görünürlük gerektirir. [Django Debug Toolbar](https://django-debug-toolbar.readthedocs.io/) ve `django-silk`, geliştirme sırasında istek düzeyinde sorgu analizi sağlar. Üretim izlemesi için yavaş sorguları günlüğe kaydedin ve serileştirme süresini takip edin: ```python # middleware.py import time import logging from django.db import connection, reset_queries from django.conf import settings logger = logging.getLogger('api.performance') class QueryCountMiddleware: def __init__(self, get_response): self.get_response = get_response def __call__(self, request): reset_queries() start = time.perf_counter() response = self.get_response(request) duration = time.perf_counter() - start query_count = len(connection.queries) if query_count > settings.QUERY_COUNT_WARNING_THRESHOLD: logger.warning( 'High query count: %d queries in %.2fs for %s %s', query_count, duration, request.method, request.path ) return response ``` `QUERY_COUNT_WARNING_THRESHOLD` değerini API karmaşıklığına göre ayarlayın—liste döndüren endpointler genellikle sayfa boyutundan bağımsız olarak 3-5 sorgu gerektirir. Kapsamlı [Django REST Framework mülakat hazırlığı](/technologies/django/interview-questions/django-rest-framework) için bu serializer kalıplarını anlamak, kıdemli mühendisleri yalnızca temel kullanımı bilenlerden ayırır. ## Sonuç - **Doğrulama sırası önemlidir**: alan düzeyinde doğrulayıcılar `validate()`'den önce çalışır, açıkça geçersiz veriler için erken başarısızlık sağlar - **Yeniden kullanılabilir doğrulayıcılar çıkarın**: `requires_context = True` ile bağımsız doğrulayıcı sınıfları, test edilebilir kalırken istek verilerine erişir - **İç içe yazmalar açık işlem gerektirir**: veri bütünlüğü için `@transaction.atomic` ile `create()` ve `update()` metodlarını geçersiz kılın - **Queryset'i serializer'a eşleştirin**: ilişkilere erişen her iç içe serializer ve `SerializerMethodField`, karşılık gelen `select_related`/`prefetch_related` gerektirir - **Hesaplama yerine anotasyon yapın**: örnek başına sorguları ortadan kaldırmak için `SerializerMethodField` hesaplamalarını queryset anotasyonlarına taşıyın - **Sorgu sayılarını izleyin**: üretim API'leri, N+1 regresyonlarını yakalamak için istek başına veritabanı sorgularını takip etmelidir --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/tr/blog/django/django-rest-framework-serializers-deep-dive