# 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. - Published: 2026-04-29 - Updated: 2026-04-29 - Author: SharpSkill - Tags: django, python, middleware, signals, interview - Reading time: 8 min --- Django'nun middleware ve signal mekanizmalari, framework'un istek isleme hattini ve bilesenler arasi iletisimi yoneten iki temel yapisal unsurdur. Middleware, her HTTP isteginin ve yanitinin gecirdigi katmanli bir boru hatti sunarken, signal'lar modeller ve uygulamalar arasinda gevsek bagli (loosely coupled) bir olay mekanizmasi saglar. Django 5.2 LTS surumunde async middleware desteginin olgunlasmasi ve signal altyapisindaki iyilestirmeler, bu iki mekanizmayi teknik mulakatlarda siklikla sorgulanan konular arasina tasimistir. Bu rehber, her iki mekanizmayi uretim ortamina uygun kod ornekleriyle ele almakta ve mulakatlarda karsilasilan kritik noktalari aydinlatmaktadir. > **Mulakat Hizli Referans Karti** > > - **Middleware**: Istek/yanit dongusu uzerinde calisan katmanli boru hatti. Her istek tum middleware zincirinden gecer. > - **Signal**: Belirli olaylarda (model kaydi, istek baslangici vb.) tetiklenen publish-subscribe mekanizmasi. > - **`process_exception`**: Yalnizca view bir istisna firlattiginda cagrilir; middleware'in `__call__` icindeki hatalar bu hook'u tetiklemez. > - **`post_save` vs `pre_save`**: `post_save` veritabani yaziminden sonra, `pre_save` yazimdan once calisir. `post_save` icinde `instance.save()` cagirmak sonsuz donguye neden olabilir. > - **Async middleware**: Django 5.2'de `async_capable = True` ve `sync_capable = False` ile tanimlanir; `get_response` `await` ile cagirilir. ## Django Middleware Nasil Calisir Django'da middleware, istek-yanit dongusunun her asamasinda devreye giren bir katmandir. Her middleware sinifi, `__init__` metodunda bir defaya mahsus yapilandirma gerceklestirir ve `__call__` metodunda her istegi isler. `get_response` parametresi, zincirdeki bir sonraki middleware'i veya nihayetinde view fonksiyonunu temsil eder. Bu tasarim, sorumluluk zinciri (chain of responsibility) deseninin Django'daki uygulamasidir. ```python # middleware.py - Basic middleware structure class SimpleMiddleware: def __init__(self, get_response): self.get_response = get_response # One-time configuration happens here at server start def __call__(self, request): # Code executed BEFORE the view (and later middleware) response = self.get_response(request) # Code executed AFTER the view (on the way back) return response ``` `__init__` metodu sunucu baslatildiginda yalnizca bir kez calisir ve burada veritabani baglantilari, onbellek istemcileri veya yapilandirma degerleri hazirlanabilir. `__call__` metodu ise her istekte cagrilir. `self.get_response(request)` satirinin oncesinde yazilan kod istek asamasinda (view'a giden yolda), sonrasinda yazilan kod ise yanit asamasinda (view'dan donen yolda) calisir. Bu iki fazli yapi, middleware'in hem istegi on-islemeden gecirmesine hem de yaniti son-islemeden gecirmesine olanak tanir. ## Istek Loglama Middleware'i Olusturma Uretim ortamlarinda her istegin sure, durum kodu ve IP adresi ile loglanmasi, performans izleme ve hata ayiklama icin kritik bir gereksinimdir. Asagidaki middleware, her istegi yapilandirilmis log formatiyla kaydeder ve `process_exception` hook'u araciligiyla view kaynakli istisnalari yakalar. ```python # apps/core/middleware.py import logging import time logger = logging.getLogger('django.request') class RequestLoggingMiddleware: def __init__(self, get_response): self.get_response = get_response def __call__(self, request): start_time = time.monotonic() # Attach metadata before view processing request.start_time = start_time response = self.get_response(request) duration = time.monotonic() - start_time logger.info( 'method=%s path=%s status=%d duration=%.3fs ip=%s', request.method, request.get_full_path(), response.status_code, duration, request.META.get('REMOTE_ADDR'), ) return response def process_exception(self, request, exception): # Called only when the view raises an exception duration = time.monotonic() - getattr(request, 'start_time', 0) logger.error( 'method=%s path=%s exception=%s duration=%.3fs', request.method, request.get_full_path(), str(exception), duration, ) return None # Let Django's default exception handling continue ``` `time.monotonic()` kullanimi, sistem saatindeki degisikliklerden etkilenmeyen hassas sure olcumu saglar. `process_exception` metodu, yalnizca view bir istisna firlattiginda devreye girer; `None` dondurerek Django'nun varsayilan istisna isleme mekanizmasinin calismaya devam etmesini saglar. `request` nesnesine `start_time` gibi ek niteliklerin eklenmesi, farkli middleware'ler arasinda veri paylasiminin yaygin bir yontemidir. ## Middleware Siralamasi ve MIDDLEWARE Ayari Middleware siralamasi, Django uygulamalarinda kritik bir yapilandirma kararidir. `MIDDLEWARE` listesindeki siralama, istek isleme sirasini dogrudan belirler: istekler listede yukaridan asagiya, yanitlar ise asagidan yukariya dogru ilerler. Guvenlik middleware'i en ustte yer almali, oturum ve kimlik dogrulama middleware'leri CSRF korumasindan sonra gelmelidir. ```python # settings.py - Middleware ordering matters MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', # Custom middleware placed after Django's core stack 'apps.core.middleware.RequestLoggingMiddleware', ] ``` Ozel middleware genellikle Django'nun cekirdek yigininin altina yerlestirilir. Bu konumlama, ozel middleware calistiginda oturum, kimlik dogrulama ve CSRF korumalarinin halihazirda uygulanmis olmasini garanti eder. Eger ozel middleware `request.user` erisimi gerektiriyorsa, `AuthenticationMiddleware` altinda konumlandirilmasi zorunludur; aksi halde `AnonymousUser` nesnesiyle karsilasilir. Mulakatlarda siklikla sorulan bir soru, middleware siralamasi degistirildiginde ne olacagidir. Ornegin, `SecurityMiddleware` listenin sonuna alinirsa, HSTS baslik ekleme ve SSL yonlendirmesi diger tum middleware'lerden sonra gerceklesir; bu da guvenlik aciklarina yol acabilir. ## Django 5.2'de Async Middleware Django 5.2, ASGI sunucularinda (Daphne, Uvicorn) calisirken tamamen asenkron middleware yazimini desteklemektedir. `async_capable = True` ve `sync_capable = False` sinif degiskenleri, Django'ya bu middleware'in yalnizca asenkron baglamda calistigini bildirir. Bu sayede `get_response` bir `awaitable` olarak kullanilabilir ve I/O islemleri bloklamadan gerceklestirilir. ```python # apps/core/middleware.py import asyncio import time class AsyncTimingMiddleware: # Mark this middleware as async-capable async_capable = True sync_capable = False def __init__(self, get_response): self.get_response = get_response async def __call__(self, request): start = time.monotonic() # get_response is awaitable in async context response = await self.get_response(request) duration = time.monotonic() - start response['X-Request-Duration'] = f'{duration:.4f}s' return response ``` Async middleware, veritabani sorgulari, harici API cagrilari veya dosya islemleri gibi I/O yogun operasyonlarda onemli performans kazanimlari saglar. `sync_capable = False` ayari, bu middleware'in senkron WSGI sunucularinda kullanilamayacagini belirtir. Hem senkron hem de asenkron ortamlarda calisabilecek bir middleware icin her iki degiskenin de `True` olarak ayarlanmasi ve `__call__` ile `async __call__` metotlarinin ayri ayri tanimlanmasi gerekir. Multakatlarda async middleware ile ilgili kritik bir soru, senkron ve asenkron middleware'lerin ayni zincirde karistirildigi durumlardadir. Django bu senaryoda otomatik adaptorler kullanir ancak her gecis bir performans maliyeti olusturur. Bu nedenle middleware zincirinin tamamen senkron veya tamamen asenkron olarak tutulmasi onerilen yaklasimdir. ## Django Signal'lari: Publish-Subscribe Deseni Django signal'lari, framework icinde yerlesik bir publish-subscribe mekanizmasi sunar. Bir bilesenin (gonderen/sender) belirli bir olayi tetiklemesi ve diger bilesenlerinin (alici/receiver) bu olaya tepki vermesi prensibine dayanir. Bu mekanizma, bilesenler arasindaki dogrudan bagimliligi ortadan kaldirarak modullerin birbirinden bagimsiz olarak gelistirilmesini ve test edilmesini kolaylastirir. Django'nun yerlesik signal'lari arasinda en sik kullanilanlar sunlardir: - **`pre_save`**: Model ornegi veritabanina yazilmadan hemen once tetiklenir. Veri dogrulama, normalizasyon ve otomatik alan doldurma icin idealdir. - **`post_save`**: Model ornegi veritabanina yazildiktan hemen sonra tetiklenir. Iliskili nesne olusturma, bildirim gonderme ve onbellek gecersiz kilma icin kullanilir. - **`pre_delete` / `post_delete`**: Silme islemi oncesinde ve sonrasinda tetiklenir. - **`request_started` / `request_finished`**: Her HTTP isteginin baslangicinda ve sonunda tetiklenir. Signal'lar ile middleware arasindaki temel fark, calisma katmanidir. Middleware HTTP istek/yanit dongusunde calisirken, signal'lar model katmaninda veya belirli framework olaylarinda devreye girer. Her iki mekanizma da farkli sorumluluk alanlarini hedefler ve birbirlerinin yerine gecmez. ## post_save ile Otomatik Profil Olusturma `post_save` signal'inin en yaygin kullanim alani, yeni bir kullanici olusturuldugunda iliskili bir profil nesnesinin otomatik olarak olusturulmasidir. `created` parametresi, signal'in bir INSERT (yeni kayit) mi yoksa UPDATE (guncelleme) mi sonucu tetiklendigini ayirt etmeye olanak tanir. ```python # apps/accounts/signals.py from django.db.models.signals import post_save from django.dispatch import receiver from django.contrib.auth import get_user_model from apps.accounts.models import UserProfile User = get_user_model() @receiver(post_save, sender=User) def create_user_profile(sender, instance, created, **kwargs): # 'created' is True only on INSERT, False on UPDATE if created: UserProfile.objects.create( user=instance, display_name=instance.get_full_name() or instance.username, ) ``` `@receiver` dekoratoru, `create_user_profile` fonksiyonunu `User` modelinin `post_save` signal'ina baglar. `created` parametresi `True` oldugunda yeni bir kullanici olusturuldugu anlasilir ve yalnizca bu durumda profil nesnesi uretilir. Bu yaklasim, profil olusturma mantgini kullanici kayit isleminden ayirarak tek sorumluluk prensibine uygun bir yapi saglar. Mulakatlarda onemli bir nokta: `post_save` icinde `instance.save()` cagirmak sonsuz bir donguye neden olur cunku `save()` yeniden `post_save` signal'ini tetikler. Bu durumdan kacinmak icin `update_fields` parametresi kullanilmali veya `QuerySet.update()` tercih edilmelidir. ## pre_save ile Veri Dogrulama ve Donusum `pre_save` signal'i, verinin veritabanina yazilmasindan hemen once mudahale etme olanagi sunar. Slug olusturma, tarih normalizasyonu ve hesaplanmis alan guncellemesi gibi islemler icin idealdir. Asagidaki ornekte, bir blog makalesinin slug alani bos birakildiysa basliktan otomatik olarak uretilmekte ve benzersizligi garanti edilmektedir. ```python # apps/blog/signals.py from django.db.models.signals import pre_save from django.dispatch import receiver from django.utils.text import slugify from apps.blog.models import Article @receiver(pre_save, sender=Article) def auto_generate_slug(sender, instance, **kwargs): if not instance.slug: base_slug = slugify(instance.title) slug = base_slug counter = 1 # Ensure slug uniqueness while Article.objects.filter(slug=slug).exclude(pk=instance.pk).exists(): slug = f'{base_slug}-{counter}' counter += 1 instance.slug = slug ``` `pre_save` signal'inin `post_save`'den temel farki, yapilan degisikliklerin ayri bir `save()` cagrisi gerektirmeden veritabanina yansitilmasidir. `instance.slug` atamasinin ardindan Django, bu guncellenenmis degeri otomatik olarak INSERT veya UPDATE sorgusuna dahil eder. Bu ozellik, sonsuz dongu riskini ortadan kaldirir ve `pre_save`'i veri donusum islemleri icin `post_save`'den daha guvenli bir tercih haline getirir. Benzersiz slug uretim algoritmasi, `exclude(pk=instance.pk)` filtresiyle mevcut kaydin kendi slug'iyla catismasini onler. Bu detay, guncelleme senaryolarinda ozellikle onemlidir. ## Signal'larin AppConfig.ready() ile Kaydedilmesi Signal receiver'larinin dogru bir sekilde kaydedilmesi, Django uygulamasinin saglikli calismasi icin kritik bir adimdir. Signal modulleri, uygulama yapilandirma sinifinin `ready()` metodu icinde import edilmelidir. Bu metot, Django baslangic surecinde tum uygulamalar yuklendikten sonra yalnizca bir kez calisir. ```python # apps/accounts/apps.py from django.apps import AppConfig class AccountsConfig(AppConfig): default_auto_field = 'django.db.models.BigAutoField' name = 'apps.accounts' def ready(self): # Import signals module to register receivers import apps.accounts.signals # noqa: F401 ``` `ready()` metodu, signal modulu icindeki `@receiver` dekoratorlerinin Django'nun signal dispatcher'ina kaydedilmesini saglar. Bu import islemi baska bir yerde (ornegin `models.py` veya `__init__.py` icinde) gerceklestirilirse, dairesel import hatalari veya signal'larin birden fazla kez kaydedilmesi gibi sorunlarla karsilasilabilir. `# noqa: F401` yorumu, linter'in kullanilmayan import uyarisini bastirmak icindir. Mulakatlarda siklikla sorulan bir hata senaryosu, `ready()` icinde signal import'unun unutulmasidir. Bu durumda `@receiver` dekoratoru hicbir zaman calistirilmaz ve signal handler'lari sessizce devre disi kalir -- uygulama hata vermez ancak beklenen davranis gerceklesmez. ## Ozel Domain Olaylari icin Custom Signal Tanimlama Django'nun yerlesik signal'larinin otesinde, uygulamaya ozgu is kurallari icin ozel signal'lar tanimlanabilir. Bu yaklasim, bir siparis tamamlandiginda farkli bilesenlerin (e-posta gonderimi, stok guncelleme, analitik kaydi) birbirinden bagimsiz olarak tepki vermesine olanak tanir. Ozel signal'lar, `django.dispatch.Signal` sinifi araciligiyla tanimlanir. ```python # apps/orders/signals.py from django.dispatch import Signal # Define custom signals with documentation order_completed = Signal() # Sent after payment confirmation order_refunded = Signal() # Sent after refund processing # apps/orders/services.py from apps.orders.signals import order_completed def complete_order(order): order.status = 'completed' order.save() # Dispatch signal with relevant context order_completed.send( sender=order.__class__, order=order, total=order.total_amount, ) # apps/notifications/receivers.py from django.dispatch import receiver from apps.orders.signals import order_completed @receiver(order_completed) def send_order_confirmation_email(sender, order, **kwargs): # Email logic decoupled from order processing from apps.notifications.services import send_email send_email( to=order.customer.email, template='order_confirmation', context={'order': order}, ) ``` Bu ornekte siparis isleme mantigi (`complete_order`) bildirim mantgindan (`send_order_confirmation_email`) tamamen ayristirilmistir. Siparis servisi yalnizca signal'i gondermekle yetinir; hangi bilesenlerin bu olaya tepki verecegini bilmesine gerek yoktur. Bu yapi, acik-kapali prensibine (Open-Closed Principle) uyumludur: yeni bir davranis eklemek icin siparis servisinin degistirilmesine gerek kalmaz, yalnizca yeni bir receiver tanimlanir. `signal.send()` metodu senkron olarak calisir ve tum receiver'larin donus degerlerini bir liste halinde dondurur. Uzun suren islemler (e-posta gonderimi, harici API cagrilari) icin receiver icinde Celery gibi bir gorev kuyrugu kullanilmasi, istek yanit suresinin uzamasini onlemek acisindan onerilen yaklasimdir. ## Mulakatlarda Sik Karsilasilan Tuzaklar Django middleware ve signal mekanizmalari, teknik mulakatlarda adaylarin derinlemesine bilgisini olcmek icin kullanilan cesitli tuzak sorulari icerir. Asagidaki iki nokta, en sik karsilasilan ve en cok hata yapilan konulari ozetlemektedir. > **QuerySet.update() Signal Tetiklemez** > > `QuerySet.update()` ve `QuerySet.delete()` toplu islemleri, model ornegi uzerinden gecmeden dogrudan SQL sorgusu calistirir. Bu nedenle `pre_save`, `post_save`, `pre_delete` ve `post_delete` signal'lari tetiklenmez. Toplu islemlerde signal'larin calismasinin beklendigi senaryolar, mulakatlarda en sik gozden kacirilan noktalardan biridir. Cozum olarak nesneler uzerinde tek tek `save()` veya `delete()` cagirilmalidir ya da `post_save` yerine veritabani trigger'lari kullanilmalidir. > **Django 5.2 LTS: Uzun Vadeli Destek** > > Django 5.2, Uzun Vadeli Destek (LTS) surumudur ve Nisan 2028'e kadar guvenlik guncellemeleri alacaktir. Uretim ortamlari icin LTS surumu tercih edilmelidir. Django 5.2 ile gelen onemli iyilestirmeler arasinda async view ve middleware desteginin olgunlasmasi, composite primary key destegi ve gelismis QuerySet yontemleri yer almaktadir. Mulakatlarda Django surum politikasi ve LTS secimi hakkinda sorular sorulabilir. Ek olarak, su tuzaklar da mulakat baglamindan siklikla karsimiza cikmaktadir: - **Signal siralama garantisi yoktur**: Ayni signal'a baglanan birden fazla receiver'in calisma sirasi tanimli degildir. Is mantigi belirli bir siralamaya bagimli olmamalidir. - **Signal'lar transaksiyonel degildir**: `post_save` signal'i, veritabani transaksiyonu commit edilmeden once calisir. `transaction.on_commit()` kullanilmazsa, receiver icindeki islemler geri alinan (rollback edilen) veriler uzerinde gerceklesebilir. - **Middleware `__init__` icinde veritabani erisimi**: `__init__` metodu sunucu baslatildiginda calisir ve bu asamada veritabani baglantisi hazir olmayabilir. Veritabani erisimi `__call__` icinde gerceklestirilmelidir. ## Middleware ve Signal Karsilastirma Tablosu | Ozellik | Middleware | Signal | |---|---|---| | Calisma katmani | HTTP istek/yanit dongusu | Model katmani ve framework olaylari | | Tetiklenme | Her HTTP isteginde | Belirli olaylarda (save, delete, request) | | Siralama | `MIDDLEWARE` listesindeki sira ile belirlenir | Tanimli bir siralama garantisi yoktur | | Kapsam | Tum isteklere uygulanir | Belirli model veya olaya baglidir | | Async destegi | Django 5.2'de tam destek | Sinirli (senkron dispatcher) | | Kullanim alani | Loglama, guvenlik, CORS, hiz sinirlandirma | Profil olusturma, bildirim, onbellek gecersiz kilma | | Test edilebilirlik | `RequestFactory` ile dogrudan test | `signal.disconnect()` ile izole test | | Performans etkisi | Her istekte calisir | Yalnizca ilgili olay gerceklestiginde calisir | ## Sonuc Django'nun middleware ve signal mekanizmalari, uygulamanin farkli katmanlarinda farkli sorumluluklar ustlenen tamamlayici yapilardir. Teknik mulakatlarda bu iki mekanizmanin derinlemesine anlasilmasi, aday degerlendirmesinde belirleyici bir rol oynamaktadir. Temel cikarimlar su sekilde ozetlenebilir: - Middleware, HTTP istek/yanit dongusunun her asamasinda calisan katmanli bir boru hatti sunar. `__call__` icindeki `get_response` cagrisi oncesi ve sonrasi, sirasiyla istek ve yanit islemlerini temsil eder. - Middleware siralamasi kritiktir: guvenlik middleware'i en ustte, ozel middleware Django'nun cekirdek yigininin altinda konumlandirilmalidir. - Django 5.2'de async middleware, ASGI sunucularinda bloklama olmadan I/O islemlerini gerceklestirmeye olanak tanir. Senkron ve asenkron middleware'lerin karistirilmasi performans maliyeti olusturur. - `pre_save` signal'i veri donusum islemleri icin idealdir cunku degisiklikler ayri bir `save()` cagrisi gerektirmez. `post_save` ise iliskili nesne olusturma ve bildirim gonderme icin uygundur. - `QuerySet.update()` ve `QuerySet.delete()` toplu islemleri signal tetiklemez -- bu, mulakatlarda en sik karsilasilan tuzaklardan biridir. - Custom signal'lar, domain olaylarini bilesenlere yaymak icin acik-kapali prensibine uygun bir mekanizma sunar. - Signal receiver'lari `AppConfig.ready()` icinde import edilmelidir; aksi halde sessizce devre disi kalir. - `post_save` icinde `transaction.on_commit()` kullanilmasi, receiver'in geri alinan veriler uzerinde calismasi riskini ortadan kaldirir. --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/tr/blog/django/django-5-2-custom-middleware-signals