【后端】【Django】Django 信号(Signals)详解

打印 上一主题 下一主题

主题 1724|帖子 1724|积分 5176

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

x
Django 信号(Signals)详解(循序渐进)

一、信号(Signal)概述

1. 什么是 Django 信号?

Django 信号(Signal) 是一种 观察者模式(Observer Pattern) 的实现,允许差别部分的代码在发生特定事件时举行通信,而不必要直接调用。
作用:当某个事件发生时,Django 会自动通知相关的信号处理函数,让它们执行相应的操作。
2. 信号的核心概念



  • 发送者(Sender):触发信号的对象(一般是 Django 模型)。
  • 信号(Signal):预定义的信号类型,比方 post_save、pre_delete 等。
  • 接收器(Receiver):监听信号的函数,接收到信号后执行相应操作。
  • 连接(Connect):将信号与接收器绑定,使其在事件发生时被触发。

二、Django 内置信号

信号名称触发时机pre_save调用 save() 之前post_save调用 save() 之后pre_delete调用 delete() 之前post_delete调用 delete() 之后m2m_changed多对多字段修改时request_startedHTTP 哀求开始时request_finishedHTTP 哀求结束时got_request_exception视图抛出非常时pre_migrate迁移前post_migrate迁移后
三、信号的根本使用

1. 监听 post_save 信号

场景: 当 User 被创建时,自动创建 Profile。
(1)创建模型

  1. from django.db import models
  2. from django.contrib.auth.models import User
  3. class Profile(models.Model):
  4.     user = models.OneToOneField(User, on_delete=models.CASCADE)
  5.     bio = models.TextField(blank=True)
复制代码
(2)在 signals.py 里定义信号

  1. from django.db.models.signals import post_save
  2. from django.dispatch import receiver
  3. from django.contrib.auth.models import User
  4. from .models import Profile
  5. @receiver(post_save, sender=User)
  6. def create_user_profile(sender, instance, created, **kwargs):
  7.     """当用户创建时,自动创建 Profile"""
  8.     if created:
  9.         Profile.objects.create(user=instance)
复制代码


  • @receiver(post_save, sender=User): 监听 User 模型的 post_save 事件。
  • created=True 时表示 新创建 的用户,此时自动创建 Profile。
(3)注册信号

在 apps.py 里:
  1. from django.apps import AppConfig
  2. class MyAppConfig(AppConfig):
  3.     default_auto_field = 'django.db.models.BigAutoField'
  4.     name = 'myapp'
  5.     def ready(self):
  6.         import myapp.signals  # 确保信号被加载
复制代码

四、信号的高级用法

1. 使用 pre_delete 软删除

场景: 当 User 被删除时,不是真正删除,而是标记为已删除
(1)修改模型

  1. class UserProfile(models.Model):
  2.     user = models.OneToOneField(User, on_delete=models.CASCADE)
  3.     is_deleted = models.BooleanField(default=False)  # 软删除标记
复制代码
(2)监听 pre_delete

  1. from django.db.models.signals import pre_delete
  2. @receiver(pre_delete, sender=User)
  3. def soft_delete_user(sender, instance, **kwargs):
  4.     """用户删除时,不真正删除,而是标记 is_deleted"""
  5.     instance.is_deleted = True
  6.     instance.save()
复制代码


  • pre_delete 触发时,不直接删除,而是修改 is_deleted。

2. 使用 m2m_changed 监听多对多关系

场景: 监听 Book 和 Author 之间的多对多关系变更。
(1)创建模型

  1. class Author(models.Model):
  2.     name = models.CharField(max_length=100)
  3. class Book(models.Model):
  4.     title = models.CharField(max_length=200)
  5.     authors = models.ManyToManyField(Author)
复制代码
(2)监听 m2m_changed

  1. from django.db.models.signals import m2m_changed
  2. @receiver(m2m_changed, sender=Book.authors.through)
  3. def book_authors_changed(sender, instance, action, **kwargs):
  4.     """当 Book 的 authors 关系发生变化时触发"""
  5.     print(f"Action: {action}, Instance: {instance}")
复制代码


  • m2m_changed 触发时,可以获取 action:

    • pre_add:添加前
    • post_add:添加后
    • pre_remove:移除前
    • post_remove:移除后
    • pre_clear:清空前
    • post_clear:清空后


五、手动连接和断开信号

1. 手动连接信号

除了使用 @receiver,我们也可以手动绑定信号:
  1. from django.db.models.signals import post_save
  2. def my_signal_handler(sender, instance, **kwargs):
  3.     print(f"{instance} 被保存了!")
  4. post_save.connect(my_signal_handler, sender=User)
复制代码
优点


  • 可以在差别的地方注册信号,灵活性更高。
2. 断开信号

偶然我们希望临时关闭某个信号:
  1. post_save.disconnect(my_signal_handler, sender=User)
复制代码
这样,post_save 信号就不会再触发 my_signal_handler。

六、信号的调试和优化

1. 克制信号循环调用

假如 save() 方法中调用 save(),大概会导致 无限循环
  1. @receiver(post_save, sender=User)
  2. def create_user_profile(sender, instance, created, **kwargs):
  3.     if created:
  4.         instance.profile = Profile.objects.create(user=instance)
  5.         instance.save()  # ⚠️ 这里会导致递归调用
复制代码
解决方案


  • 在 save() 方法里 禁用信号
  1. from django.db.models.signals import post_save
  2. @receiver(post_save, sender=User)
  3. def create_user_profile(sender, instance, created, **kwargs):
  4.     if created:
  5.         post_save.disconnect(create_user_profile, sender=User)
  6.         instance.profile = Profile.objects.create(user=instance)
  7.         instance.save()
  8.         post_save.connect(create_user_profile, sender=User)
复制代码

2. 使用 weak=False 克制接收器被垃圾回收

默认环境下,@receiver 使用弱引用,假如接收器没有其他引用,大概会被回收:
  1. post_save.connect(my_signal_handler, sender=User, weak=False)
复制代码

七、总结

知识点说明post_save在模型 save() 之后触发pre_save在模型 save() 之前触发post_delete在 delete() 之后触发pre_delete在 delete() 之前触发m2m_changed监听多对多关系变更@receiver绑定信号的装饰器connect()手动绑定信号disconnect()手动解绑信号克制循环调用在 save() 里 断开信号 处理 Django 信号提供了强大的事件监听能力,可以资助我们自动执行任务、减少代码耦合,但必要注意克制循环调用、调试信号、提高性能
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

东湖之滨

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表