Django 信号指南

Django 信号系统让解耦的组件响应事件。学习 pre/post save、delete 信号、自定义信号和安全模式。

1. 内置模型信号

from django.db.models.signals import pre_save, post_save, post_delete
from django.dispatch import receiver

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

@receiver(pre_save, sender=Article)
def auto_slug(sender, instance, **kwargs):
    if not instance.slug:
        from django.utils.text import slugify
        instance.slug = slugify(instance.title)

@receiver(post_delete, sender=Article)
def delete_cover_image(sender, instance, **kwargs):
    if instance.cover_image:
        instance.cover_image.delete(save=False)

# 在 AppConfig.ready() 中注册
class MyAppConfig(AppConfig):
    name = "myapp"
    def ready(self):
        import myapp.signals  # noqa

2. 安全信号模式

from django.db import transaction

# 使用 on_commit 避免竞态条件
@receiver(post_save, sender=Order)
def notify_fulfillment(sender, instance, created, **kwargs):
    if created:
        transaction.on_commit(lambda: process_order.delay(instance.pk))

# 避免无限循环 — 检查 update_fields
@receiver(post_save, sender=Article)
def update_stats(sender, instance, **kwargs):
    update_fields = kwargs.get("update_fields")
    if update_fields is None or "status" in update_fields:
        # 执行统计更新
        pass

3. 自定义信号

from django.dispatch import Signal

article_published = Signal()

def publish_article(article, user):
    article.status = "published"
    article.save(update_fields=["status"])
    article_published.send(sender=Article, article=article, publisher=user)

@receiver(article_published)
def on_article_published(sender, article, publisher, **kwargs):
    Notification.objects.create(
        user=article.author,
        message=f'您的文章"{article.title}"已由 {publisher} 发布',
    )

4. 断开信号

from django.db.models.signals import post_save

def my_handler(sender, instance, **kwargs):
    print(f"已保存: {instance}")

post_save.connect(my_handler, sender=MyModel)
post_save.disconnect(my_handler, sender=MyModel)

5. 内置信号参考

信号触发时机
pre_savemodel.save() 之前
post_savemodel.save() 之后
pre_deletemodel.delete() 之前
post_deletemodel.delete() 之后
m2m_changed多对多关系变更
user_logged_in用户登录成功
request_startedHTTP 请求开始