semua tentang signals pada django secara singkat dan padat

kholil 26 August 2025 Python 13 views 0 comment 3 minutes
Gambar Artikel

Apa itu Django Signals?

Django Signals adalah sistem notifikasi yang memungkinkan aplikasi untuk berkomunikasi secara loosely coupled. Signals memungkinkan kode tertentu dijalankan ketika tindakan tertentu terjadi di tempat lain dalam aplikasi.

Jenis-jenis Signals

Model Signals

1. pre_save

Dikirim sebelum model disimpan ke database.

from django.db.models.signals import pre_save
from django.dispatch import receiver
from myapp.models import MyModel

@receiver(pre_save, sender=MyModel)
def my_handler(sender, instance, **kwargs):
    # Logika sebelum save
    instance.slug = instance.title.lower().replace(' ', '-')

2. post_save

Dikirim setelah model disimpan ke database.

from django.db.models.signals import post_save

@receiver(post_save, sender=MyModel)
def create_profile(sender, instance, created, **kwargs):
    if created:
        # Logika setelah objek baru dibuat
        UserProfile.objects.create(user=instance)

3. pre_delete

Dikirim sebelum model dihapus.

from django.db.models.signals import pre_delete

@receiver(pre_delete, sender=MyModel)
def backup_before_delete(sender, instance, **kwargs):
    # Backup data sebelum dihapus
    BackupModel.objects.create(original_data=instance.data)

4. post_delete

Dikirim setelah model dihapus.

from django.db.models.signals import post_delete

@receiver(post_delete, sender=MyModel)
def cleanup_files(sender, instance, **kwargs):
    # Hapus file terkait
    if instance.image:
        instance.image.delete(save=False)

5. m2m_changed

Dikirim ketika ManyToManyField berubah.

from django.db.models.signals import m2m_changed

@receiver(m2m_changed, sender=MyModel.tags.through)
def tags_changed(sender, instance, action, pk_set, **kwargs):
    if action == "post_add":
        # Logika ketika tag ditambahkan
        pass

Cara Implementasi

1. Menggunakan Decorator @receiver

from django.db.models.signals import post_save
from django.dispatch import receiver

@receiver(post_save, sender=User)
def user_saved(sender, instance, created, **kwargs):
    if created:
        print(f"User baru: {instance.username}")

2. Menggunakan connect()

from django.db.models.signals import post_save

def user_saved(sender, instance, created, **kwargs):
    if created:
        print(f"User baru: {instance.username}")

post_save.connect(user_saved, sender=User)

3. Registrasi di apps.py

# apps.py
from django.apps import AppConfig

class MyAppConfig(AppConfig):
    default_auto_field = 'django.db.models.BigAutoField'
    name = 'myapp'

    def ready(self):
        import myapp.signals  # Import file signals

Best Practices

1. Pisahkan File Signals

Buat file signals.py terpisah untuk menjaga kode tetap terorganisir.

# signals.py
from django.db.models.signals import post_save
from django.dispatch import receiver
from .models import MyModel

@receiver(post_save, sender=MyModel)
def handle_save(sender, instance, created, **kwargs):
    # Handler logic
    pass

2. Hindari Infinite Loops

@receiver(post_save, sender=MyModel)
def avoid_loop(sender, instance, created, **kwargs):
    if not hasattr(instance, '_signal_processed'):
        instance._signal_processed = True
        # Proses yang aman
        instance.save()  # Ini bisa menyebabkan loop

3. Gunakan weak=False untuk Persistent Connections

post_save.connect(my_handler, sender=MyModel, weak=False)

4. Handle Exceptions

@receiver(post_save, sender=MyModel)
def safe_handler(sender, instance, created, **kwargs):
    try:
        # Operasi yang mungkin gagal
        external_api_call(instance.data)
    except Exception as e:
        logger.error(f"Signal handler error: {e}")

Custom Signals

Membuat Signal Custom

# signals.py
import django.dispatch

# Definisi signal custom
user_logged_in = django.dispatch.Signal()

# Mengirim signal
user_logged_in.send(sender=self.__class__, user=user, request=request)

# Menerima signal
@receiver(user_logged_in)
def handle_user_login(sender, user, request, **kwargs):
    # Logika custom
    UserActivity.objects.create(user=user, activity='login')

Contoh Penggunaan Real-world

1. Auto-generate Slug

@receiver(pre_save, sender=Article)
def generate_slug(sender, instance, **kwargs):
    if not instance.slug:
        instance.slug = slugify(instance.title)

2. Send Email Notification

@receiver(post_save, sender=Order)
def send_order_email(sender, instance, created, **kwargs):
    if created:
        send_mail(
            'Order Confirmation',
            f'Your order #{instance.id} has been created.',
            '[email protected]',
            [instance.user.email],
        )

3. Update Cache

@receiver(post_save, sender=Product)
def invalidate_cache(sender, instance, **kwargs):
    cache.delete(f'product_{instance.id}')
    cache.delete('products_list')

4. Create Related Objects

@receiver(post_save, sender=User)
def create_user_profile(sender, instance, created, **kwargs):
    if created:
        UserProfile.objects.create(user=instance)

Tips dan Warnings

⚠️ Perhatian

  • Signals dapat memperlambat operasi database
  • Hindari operasi heavy di dalam signal handlers
  • Berhati-hati dengan signal yang memanggil save() lagi
  • Signals tidak dipanggil untuk bulk operations (bulk_create, bulk_update)

💡 Tips

  • Gunakan created parameter di post_save untuk membedakan create vs update
  • Test signal handlers secara terpisah
  • Pertimbangkan menggunakan Celery untuk operasi asynchronous
  • Dokumentasikan semua signal handlers dalam proyek

Testing Signals

# test_signals.py
from django.test import TestCase
from django.db.models.signals import post_save
from unittest.mock import patch

class SignalTest(TestCase):
    @patch('myapp.signals.send_notification')
    def test_signal_called(self, mock_send):
        user = User.objects.create(username='test')
        mock_send.assert_called_once()

    def test_signal_disconnected(self):
        # Disconnect signal untuk test ini
        post_save.disconnect(create_profile, sender=User)
        user = User.objects.create(username='test')
        # Assert profile tidak dibuat
        self.assertFalse(hasattr(user, 'profile'))

Kesimpulan

Django Signals adalah tool powerful untuk implementasi loosely coupled architecture. Gunakan dengan bijak untuk menjaga performa aplikasi tetap optimal.

Komentar (0)

Tinggalkan Komentar