İçeriğe geç

Django Signals

Herkese merhabalar.

Django, ayrıştırılmış uygulamalarda belirli eylemler gerçekleştiğinde, gerçekleşen eylemden haberdar olmanızı sağlayan yerleşik bir sinyal dağıtıcısına sahiptir.

Eylemleri dinleyerek belirli durumlarda bağlı olması gereken işlemleri tetikleyebilirsiniz.

Örneğin kullanıcı ilk defa kayıt olduğunda otomatik olarak profilini oluşturmak, 3.parti uygulamalarda gerçekleşecek eylemlere bağlı işlemleri tetiklemek veya güncellediğiniz nesnenin değişen değerlerine göre kullanıcınızı email yoluyla bilgilendirmek isteyebilirsiniz.


Django, geliştiriciyi uygulama genelinde gerçekleşen eylemlerden haberdar etmek için bir çok yerleşik sinyal barındırır ve yollar.

Bu sinyaller modellere bağlı aksiyonlar ve HTTP isteklerinin bildirilmesi şeklinde 2’ye ayrılabilir.

  • pre_save, post_save (modelin save metodunun çağrılmasından önce ve sonra)
  • pre_delete, post_delete (modelin delete metodunun çağrılmasından önce ve sonra)
  • m2m_changed (modelin m2m alanı değiştiğinde)
  • request_started, request_finished (HTTP isteği başladığında, ve bittiğinde.)
  • Ayrıca yerleşik Django auth uygulamasını kullanmakta iseniz kullanıcı giriş, çıkış ve başarısız giriş denemelerinde (user_logged_in, user_logged_out, user_login_failed)

Bunların yanında kendi sinyallerinizi de oluşturup kullanabilirsiniz.

Nasıl Çalışır?

Sinyal mekanizmasının çalışma mantığında 2 ana unsur bulunur. Gönderici(Sender) ve Alıcı(Receiver).

En kısa haliyle gönderici bir eylem sonucunda sinyal göndermekle yükümlüdür.

Alıcı ise bu aldıktan sonra yürütülmesi gereken işlemleri gerçekleştirmek ile yükümlüdür.


Örnek bir kullanım olarak:

from django.db.models.signals import post_save

def do_stuff(sender,**kwargs):
    print('called sender model save method.')

post_save.connect(do_stuff,sender=SenderModel)

Yukarıdaki örnekte sender olarak tanımlanan modelin save metodu çağrıldığında do_stuff fonksiyonu çağrılacak ve konsola bilgi mesajı geçilecektir.

Diğer bir kullanım yöntemi ise receiver dekoratörü ile kullanmak.

@receiver(post_save, sender=SenderModel)
def do_stuff_2(sender, instance, **kwargs):
    print("called sender model save method")

İki yöntemde tamamiyle birbiri ile aynı işi yapmakta.


Örnek bir senaryo üstünden gidecek olursak kullanıcı modelimizi genişlettiğimizi düşünelim. Ve kullanıcı kayıt olduğunda profili otomatik olarak oluşturulsun.

from django.db.models.signals import post_save
from django.dispatch import receiver
from django.contrib.auth import get_user_model
from profiles.models import Profile

User = get_user_model()


@receiver(post_save, sender=User)
def create_profile_on_registration(sender, instance, created, **kwargs):
    "Create the profile when the user registers for the first time."
    if created:
        Profile.objects.create(user=instance)

Yukarıdaki alıcı kullanıcı ilk defa kayıt olduğunda otomatik olarak profilini oluşturacaktır.

Ayrıca her sinyal alıcı fonksiyon da kullanılması adına farklı argümanlar sağlar. Örneğin post_save sinyali oluşturulan nesneyi işaret etmek için instance, ilk defa oluşturulup oluşturulmadığını bildiren created bool ve kayıt esnasında değişen fieldların kontrolleri için update_fields gibi.

Daha detaylı bilgi için.

Sinyallerin kayıt edilmesi.

Sinyaller Django projenizin herhangi bir yerinde yaşayabilir. Fakat yönetim kolaylığı açısından genellikle ilgili uygulama altında signals.py adlı bir dosyada barındırılır.

Dinlenecek sinyaller ve oluşturulan alıcılardan Django’yu haberdar etmemiz gerekir.

İlk olarak uygulama yapılandırma dosyası apps.py ‘de uygulamanın hazır olduğunda sinyalleri içeri dahil etmesini söylememiz gerekmekte.

from django.apps import AppConfig

class ProfilesConfig(AppConfig):
    name = "profiles"

    def ready(self):
        import profiles.signals

Yukarıdaki kullanım receiver dekoratörü ile kullanım için. Eğer bu dekoratörü kullanmıyor iseniz şu sekilde sinyallerinizi kayıt edebilirsiniz.

from django.apps import AppConfig

class ProfilesConfig(AppConfig):
    name = "profiles"

    def ready(self):
        from profiles.signals import create_profile_on_registration, do_stuff
        from django.contrib.auth.models import User
        from django.db.models.signals import post_save

        post_save.connect(create_profile_on_registration, User)
        post_save.connect(do_stuff, User)

Burada dikkat etmemiz gereken önemli bir nokta var. Uygulamanın nasıl settings.py’ de nasıl kayıt edildiği.

Eğer direkt uygulama ismi ile uygulamayı kayıt etmişseniz

INSTALLED_APPS = [
     ...
    "profiles",
]

uygulamanın __init__py dosyasında yapılandırma sınıfını açıkça belirtmelisiniz.

default_app_config = "profiles.apps.ProfilesConfig"

INSTALLED_APPS = [
    ....
    "profiles.apps.ProilesConfig",
]

Uygulamayı kayıt ederken yolu belirtmiş iseniz yukarıdaki işlemi yapmanıza gerek kalmayacaktır.

Basit bir kullanım ile sinyallerinizi test edebilirsiniz.

from django.test import TestCase
from profiles.models import Profile
from django.contrib.auth import get_user_model

User = get_user_model()

class SignalTests(TestCase):
    def test_has_profile(self):
        user = User.objects.create_user(username="test", password="supersecret")
        self.assertTrue(hasattr(user, "profile"))

Son olarak sinyaller bazen birden fazla kez yayınlanabilir ve alıcı fonksiyon gereksiz çalışabilir. (Özellikle testlerde) Bunun önüne geçmek için unique bir uid verebilirsiniz.

@receiver(post_save, sender=User, dispatch_uid="unique_identifier")
....

Daha fazla bilgi için dökümantasyon linki burada. Herkese iyi günler!

 

Tarih:Blog

Tek Yorum

  1. Merve Alpay Merve Alpay

    Çok güzel bir yazı olmuş elinize sağlık 🙂 DRF ile ilgili yazılarınıza devam etmelisiniz! 🙂

Bir cevap yazın

E-posta hesabınız yayımlanmayacak.

Göster
Gizle