from django.db.models.signals import post_save
from django.dispatch import receiver
@receiver(post_save, sender=Order)
def notify_on_order_created(sender, instance, created, **kwargs):
if created: # False on updates, True only on the initial insert
send_confirmation_email(instance)
Signals let decoupled code react to an event (a model saving, a user logging in) without the code that triggers the event needing to know anything about the listener. They're useful for genuinely cross-cutting side effects (audit logging, cache invalidation), but overusing them is a well-known Django anti-pattern: logic that's scattered across signal handlers instead of living visibly in the view or model method that triggers it becomes much harder to trace — "why did this row change?" now requires knowing every signal receiver exists, rather than reading top to bottom.