Signals là cơ chế observer của Django: gắn listener vào một sự kiện (model lưu, xoá, request bắt đầu) — sender phát signal, mọi receiver chạy đồng bộ ngay sau đó, trừ khi bạn tự đẩy qua task queue.
# apps/blog/signals.py
from django.db.models.signals import post_save
from django.dispatch import receiver
@receiver(post_save, sender=Post)
def notify_subscribers(sender, instance, created, **kwargs):
if created:
send_email_async.delay(instance.id)
# apps/blog/apps.py
class BlogConfig(AppConfig):
def ready(self):
from . import signals # nạp khi Django bootMấy signal built-in hay dùng: pre_save, post_save, pre_delete, post_delete, m2m_changed, request_started, request_finished.
Signal chạy đồng bộ trong cùng transaction với code phát sender, nên lỗi trong receiver sẽ rollback luôn cả save() gốc. Khi cần side-effect ngoài DB (gửi email, gọi API), bọc bằng transaction.on_commit(lambda: ...) để chỉ chạy sau khi commit thành công — tránh tình trạng email đã bay đi mà transaction lại rollback.
Signals are Django's observer mechanism: attach listeners to events (model save, delete, request start) — the sender fires the signal, all receivers run synchronously right after (unless you push them onto a task queue).
# apps/blog/signals.py
from django.db.models.signals import post_save
from django.dispatch import receiver
@receiver(post_save, sender=Post)
def notify_subscribers(sender, instance, created, **kwargs):
if created:
send_email_async.delay(instance.id)
# apps/blog/apps.py
class BlogConfig(AppConfig):
def ready(self):
from . import signals # load when Django bootsCommon built-in signals: pre_save, post_save, pre_delete, post_delete, m2m_changed, request_started, request_finished.
Pitfall: Signals run synchronously inside the same transaction as the sender code. A receiver exception → rollbacks the original save(). For non-DB side effects (email, API call), wrap with transaction.on_commit(lambda: ...) so they only run after a successful commit.