概述 Django信号系统是一个基于观察者模式的事件系统,允许在特定事件发生时自动执行相关代码。它实现了应用组件之间的解耦,使得代码更加模块化和可维护。
核心概念 1. 信号(Signal) 信号是Django中的事件通知机制,当某个动作发生时,信号会被发送给所有注册的接收器。
2. 发送器(Sender) 发送信号的对象,通常是Django模型类或None(表示任何发送器)。
3. 接收器(Receiver) 响应信号的函数或方法,当信号被发送时会被自动调用。
4. 连接(Connect) 将接收器注册到特定信号的过程。
工作原理 基本流程 1 2 3 4 5 1. 注册阶段(connect) 用户定义处理函数 → 连接到信号 → 存储到接收器列表2. 触发阶段(send) 事件发生 → Django发送信号 → 查找接收器 → 调用处理函数
核心机制 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 class SimpleSignal : def __init__ (self ): self .receivers = [] def connect (self, receiver, sender=None ): lookup_key = (id (receiver), id (sender)) receiver_ref = weakref.ref(receiver) self .receivers.append((lookup_key, receiver_ref)) def send (self, sender, **kwargs ): results = [] for key, ref in self .receivers: if key[1 ] == id (sender): receiver = ref() if receiver: result = receiver(sender=sender, **kwargs) results.append((receiver, result)) return results
Django内置信号 模型信号 pre_save / post_save 1 2 3 4 5 6 7 8 9 10 11 12 13 14 from django.db.models.signals import pre_save, post_savefrom django.contrib.auth.models import Userdef user_pre_save (sender, instance, **kwargs ): print (f"即将保存用户: {instance.username} " )def user_post_save (sender, instance, created, **kwargs ): if created: print (f"新用户创建: {instance.username} " ) else : print (f"用户更新: {instance.username} " ) pre_save.connect(user_pre_save, sender=User) post_save.connect(user_post_save, sender=User)
pre_delete / post_delete 1 2 3 4 5 6 7 8 9 10 from django.db.models.signals import pre_delete, post_deletedef user_pre_delete (sender, instance, **kwargs ): print (f"即将删除用户: {instance.username} " )def user_post_delete (sender, instance, **kwargs ): print (f"用户已删除: {instance.username} " ) pre_delete.connect(user_pre_delete, sender=User) post_delete.connect(user_post_delete, sender=User)
m2m_changed 1 2 3 4 5 6 7 8 9 from django.db.models.signals import m2m_changeddef groups_changed (sender, instance, action, pk_set, **kwargs ): if action == "post_add" : print (f"用户 {instance.username} 加入了新组" ) elif action == "post_remove" : print (f"用户 {instance.username} 离开了组" ) m2m_changed.connect(groups_changed, sender=User.groups.through)
请求/响应信号 request_started / request_finished 1 2 3 4 5 6 7 8 9 10 from django.core.signals import request_started, request_finisheddef request_start_handler (sender, environ, **kwargs ): print ("请求开始处理" )def request_finish_handler (sender, **kwargs ): print ("请求处理完成" ) request_started.connect(request_start_handler) request_finished.connect(request_finish_handler)
实现细节 1. 数据结构 1 2 3 4 5 class Signal : def __init__ (self ): self .receivers = [] self .lock = threading.RLock() self .sender_receivers_cache = weakref.WeakKeyDictionary()
2. 连接器实现 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 def connect (self, receiver, sender=None , weak=True , dispatch_uid=None ): if settings.DEBUG: assert callable (receiver), "接收器必须是可调用对象" if not func_accepts_kwargs(receiver): raise ValueError("接收器必须接受关键字参数" ) if dispatch_uid: lookup_key = (dispatch_uid, _make_id(sender)) else : lookup_key = (_make_id(receiver), _make_id(sender)) if weak: if hasattr (receiver, '__self__' ) and hasattr (receiver, '__func__' ): receiver_ref = WeakMethod(receiver) else : receiver_ref = weakref.ref(receiver, self ._remove_receiver) else : receiver_ref = receiver with self .lock: for r_key, _ in self .receivers: if r_key == lookup_key: return self .receivers.append((lookup_key, receiver_ref)) self .sender_receivers_cache.clear()
3. 发送器实现 1 2 3 4 5 6 7 8 9 10 def send (self, sender, **named ): if not self .receivers or self .sender_receivers_cache.get(sender) is NO_RECEIVERS: return [] return [ (receiver, receiver(signal=self , sender=sender, **named)) for receiver in self ._live_receivers(sender) ]
4. 弱引用机制 为什么使用弱引用? 1 2 3 4 5 6 7 8 9 10 11 12 class MyClass : def handler (self ): pass obj = MyClass() signal.connect(obj.handler) del obj signal.connect(obj.handler, weak=True ) del obj
绑定方法的特殊处理 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 def my_function (): pass ref = weakref.ref(my_function) class MyClass : def my_method (self ): pass obj = MyClass() method = obj.my_method ref = weakref.ref(method) ref = WeakMethod(obj.my_method)
5. 性能优化 缓存机制 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 def _live_receivers (self, sender ): receivers = self .sender_receivers_cache.get(sender) if receivers is None : receivers = [] sender_id = _make_id(sender) for (r_key, r_ref) in self .receivers: if r_key[1 ] == sender_id or r_key[1 ] == _make_id(None ): receiver = self ._get_receiver(r_ref) if receiver: receivers.append(receiver) if receivers: self .sender_receivers_cache[sender] = receivers else : self .sender_receivers_cache[sender] = NO_RECEIVERS return receivers
早期退出 1 2 3 4 5 6 7 8 9 def send (self, sender, **named ): if not self .receivers: return [] if self .sender_receivers_cache.get(sender) is NO_RECEIVERS: return []
使用模式 1. 基本用法 1 2 3 4 5 6 7 8 9 10 11 from django.db.models.signals import post_savefrom django.contrib.auth.models import Userdef welcome_new_user (sender, instance, created, **kwargs ): if created: print (f"欢迎新用户: {instance.username} " ) post_save.connect(welcome_new_user, sender=User)
2. 使用装饰器 1 2 3 4 5 6 from django.dispatch import receiver@receiver(post_save, sender=User ) def user_post_save_handler (sender, instance, created, **kwargs ): if created: print (f"新用户创建: {instance.username} " )
3. 防止重复连接 1 2 3 4 5 6 7 8 9 10 11 12 post_save.connect( welcome_new_user, sender=User, dispatch_uid="welcome_new_user_unique" ) post_save.connect( welcome_new_user, sender=User, dispatch_uid="welcome_new_user_unique" )
4. 手动断开连接 1 2 3 4 5 6 7 8 post_save.connect(my_handler, sender=User) post_save.disconnect(my_handler, sender=User) post_save.disconnect(sender=User, dispatch_uid="my_unique_handler" )
5. 自定义信号 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 import django.dispatch user_logged_in = django.dispatch.Signal()@receiver(user_logged_in ) def handle_user_login (sender, user, ip_address, **kwargs ): print (f"用户 {user.username} 从 {ip_address} 登录" )def login_view (request ): user_logged_in.send( sender=None , user=request.user, ip_address=request.META.get('REMOTE_ADDR' ) )
错误处理 send vs send_robust send方法(快速失败) 1 2 3 4 5 6 def send (self, sender, **named ): return [ (receiver, receiver(signal=self , sender=sender, **named)) for receiver in self ._live_receivers(sender) ]
send_robust方法(容错处理) 1 2 3 4 5 6 7 8 9 10 def send_robust (self, sender, **named ): responses = [] for receiver in self ._live_receivers(sender): try : response = receiver(signal=self , sender=sender, **named) except Exception as err: response = err responses.append((receiver, response)) return responses
最佳实践 1. 接收器设计原则 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 def good_receiver (sender, instance, created, **kwargs ): """ 良好的接收器设计: 1. 接受**kwargs参数 2. 处理业务逻辑简洁 3. 避免修改传入的对象 4. 异常处理得当 """ if created: try : send_welcome_email(instance) except Exception as e: logger.error(f"发送欢迎邮件失败: {e} " )
2. 避免循环调用 1 2 3 4 5 6 7 8 9 10 11 12 @receiver(post_save, sender=User ) def update_user_profile (sender, instance, **kwargs ): instance.last_updated = timezone.now() instance.save() @receiver(post_save, sender=User ) def update_user_profile (sender, instance, **kwargs ): User.objects.filter (pk=instance.pk).update( last_updated=timezone.now() )
3. 性能考虑 1 2 3 4 5 6 7 8 9 10 @receiver(post_save, sender=User ) def heavy_task_handler (sender, instance, created, **kwargs ): if created: send_complex_email(instance) from celery import current_app current_app.send_task('send_welcome_email' , [instance.id ])
4. 测试信号 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 from django.test import TestCasefrom django.db.models.signals import post_saveclass SignalTestCase (TestCase ): def test_user_creation_signal (self ): with self .assertSignalSent(post_save, sender=User): User.objects.create_user(username='test' ) post_save.disconnect(welcome_new_user, sender=User) try : user = User.objects.create_user(username='test' ) finally : post_save.connect(welcome_new_user, sender=User)
总结 Django信号系统的核心机制:
注册阶段 :connect方法将接收器函数与信号关联,存储为弱引用
触发阶段 :send方法根据发送者查找对应接收器并调用
内存管理 :弱引用确保对象可以正常被垃圾回收
性能优化 :缓存机制和早期退出提高执行效率
线程安全 :使用锁保护共享数据结构
信号系统实现了松耦合的事件驱动架构,是Django框架中优雅的设计模式之一。正确使用信号可以让代码更加模块化和可维护。