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.

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.
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.
# 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 attrsBu 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.
# 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.
# 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.
# 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:
# 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.
İç 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.
Django mülakatlarında başarılı olmaya hazır mısın?
İnteraktif simülatörler, flashcards ve teknik testlerle pratik yap.
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ı 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:
# 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:
# views.py - OPTIMIZED: 3 queries total
class BookListView(generics.ListAPIView):
queryset = Book.objects.select_related('author').prefetch_related('chapters')
serializer_class = BookSerializerKoşullu mantık içeren karmaşık serializerlar için, serializer gereksinimlerini karşılamak üzere get_queryset() metodunu geçersiz kılın:
# 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.
# 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:
# views.py
from django.db.models import Sum
class AuthorListView(generics.ListAPIView):
queryset = Author.objects.annotate(
total_sales=Sum('books__sales')
)
serializer_class = AuthorSerializer# 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.
# 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.
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 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:
# 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 responseQUERY_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ığı 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 = Trueile 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.atomicilecreate()veupdate()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 gelenselect_related/prefetch_relatedgerektirir - Hesaplama yerine anotasyon yapın: örnek başına sorguları ortadan kaldırmak için
SerializerMethodFieldhesaplamaları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
Pratik yapmaya başla!
Mülakat simülatörleri ve teknik testlerle bilgini test et.
Etiketler
Paylaş
İlgili makaleler

Django Async View'ler ve ASGI (2026): Performans ve Mülakat Soruları
2026'da Django async view'ler ve ASGI üzerine derinlemesine bir inceleme: perde arkasında nasıl çalıştıkları, hangi sunucunun tercih edileceği, async ORM ile SynchronousOnlyOperation tuzağı ve mülakat soruları.

Django Mülakat Soruları: ORM, Middleware ve DRF Derinlemesine İnceleme
Django mülakat soruları: select_related ve prefetch_related ile ORM optimizasyonu, middleware mimarisi ve Django REST Framework serializer performansı, izinler ve sayfalama kalıpları.

Django ve PostgreSQL 2026: İndeksleme, Tam Metin Arama ve Mülakat Soruları
Django PostgreSQL optimizasyonu için pratik bir rehber: B-tree, kısmi ve kapsayan indeksler, SearchVector ve GIN ile tam metin arama, ayrıca 2026 mülakat soruları.