找回密码
 立即注册
首页 业界区 安全 Django信号

Django信号

炳裘垦 2025-9-30 11:44:10
1、基本概念

Django 信号是一种观察者模式的实现,用于在框架内部或应用之间传递信息,实现解耦的组件通信。当特定事件发生时(如模型保存、删除等),信号系统会通知所有已注册的接收器。
组成:

  • 发送者 (Sender):触发信号的对象,通常是模型类,但也可以是任何 Python 对象。
  • 信号 (Signal):事件本身的载体,是 django.dispatch.Signal的实例。
  • 接收者 (Receiver):响应信号的函数或方法,在信号发送时被调用。
2、内置信号

2.1 模型信号
  1. from django.db.models.signals import (
  2.     pre_init, post_init,          # 模型实例化前后
  3.     pre_save, post_save,          # 模型保存前后
  4.     pre_delete, post_delete,      # 模型删除前后
  5.     m2m_changed,                  # 多对多关系变更
  6.     class_prepared                # 模型类准备就绪
  7. )
复制代码
2.2 请求/响应信号
  1. from django.core.signals import (
  2.     request_started,              # 请求开始
  3.     request_finished,             # 请求结束
  4.     got_request_exception         # 请求异常
  5. )
复制代码
2.3 数据库包装器信号
  1. from django.db.backends.signals import (
  2.     connection_created            # 数据库连接创建
  3. )
复制代码
3、源码分析

3.1 Signal简化版代码
  1. class Signal:
  2.     def __init__(self, use_caching=False):
  3.         # 接收者列表
  4.         self.receivers = []
  5.         # 是否使用缓存
  6.         self.use_caching = use_caching
  7.         # 用于优化的缓存
  8.         self.sender_receivers_cache = weakref.WeakKeyDictionary() if use_caching else {}
  9.     def connect(self, receiver, sender=None, weak=True, dispatch_uid=None):
  10.         """注册接收器"""
  11.         # 获取或创建dispatch_uid
  12.         if dispatch_uid:
  13.             lookup_key = (dispatch_uid, _make_id(sender))
  14.         else:
  15.             lookup_key = (_make_id(receiver), _make_id(sender))
  16.         # 如果是弱引用,使用weakref
  17.         if weak:
  18.             ref = weakref.ref
  19.             receiver_object = receiver
  20.             if hasattr(receiver, "__self__") and hasattr(receiver, "__func__"):
  21.                 ref = weakref.WeakMethod
  22.                 receiver_object = receiver.__self__
  23.             receiver = ref(receiver)
  24.             weakref.finalize(receiver_object, self._remove_receiver)
  25.         # 将接收器添加到列表
  26.         self.receivers.append((lookup_key, receiver, is_async))
  27.     def disconnect(self, receiver=None, sender=None, dispatch_uid=None):
  28.         """
  29.         断开接收者与信号的连接, 清理缓存和self.receivers
  30.         """
  31.         if dispatch_uid:
  32.             lookup_key = (dispatch_uid, _make_id(sender))
  33.         else:
  34.             lookup_key = (_make_id(receiver), _make_id(sender))
  35.         disconnected = False
  36.         with self.lock:
  37.             self._clear_dead_receivers()
  38.             for index in range(len(self.receivers)):
  39.                 r_key, *_ = self.receivers[index]
  40.                 if r_key == lookup_key:
  41.                     disconnected = True
  42.                     del self.receivers[index]
  43.                     break
  44.             self.sender_receivers_cache.clear()
  45.         return disconnected
  46.     def has_listeners(self, sender=None):
  47.         sync_receivers, async_receivers = self._live_receivers(sender)
  48.         return bool(sync_receivers) or bool(async_receivers)
  49.     def send(self, sender, **named):
  50.         """
  51.         发送信号给所有连接的接收者
  52.         """
  53.         sync_receivers = self._live_receivers(sender)
  54.         for receiver in sync_receivers:
  55.             # 调用接收器
  56.             response = receiver(signal=self, sender=sender, **named)
  57.             responses.append((receiver, response))
  58.         return responses
  59.    
  60.     def send_robust(self, sender, **named):
  61.         """
  62.         发送信号,即使某些接收者抛出异常也继续执行
  63.         """
  64.         pass
  65.     def _live_receivers(self, sender):
  66.         """
  67.         获取活动的接收者
  68.         """
  69.         pass
复制代码
3.2 调用流程
  1. class ModelBase(type):
  2.     """Model的元类,控制Model类的创建"""
  3.     def __new__(cls, name, bases, attrs, **kwargs):
  4.         pass
  5.         
  6.     def _prepare(cls):
  7.         # ...
  8.         # 模型类准备就绪
  9.         class_prepared.send(sender=cls)
  10. class Model(AltersData, metaclass=ModelBase):
  11.     def __init__(self, *args, **kwargs):
  12.         # 模型实例化前
  13.         pre_init.send(sender=cls, args=args, kwargs=kwargs)
  14.         # ...
  15.         # 模型实例化后
  16.         post_init.send(sender=cls, instance=self)
  17.     def save_base(
  18.         self,
  19.         raw=False,
  20.         force_insert=False,
  21.         force_update=False,
  22.         using=None,
  23.         update_fields=None,
  24.     ):
  25.         if not meta.auto_created:
  26.             # 模型保存前
  27.             pre_save.send(
  28.                 sender=origin,
  29.                 instance=self,
  30.                 raw=raw,
  31.                 using=using,
  32.                 update_fields=update_fields,
  33.             )
  34.         # ...
  35.         if not meta.auto_created:
  36.             # 模型保存后
  37.             post_save.send(
  38.                 sender=origin,
  39.                 instance=self,
  40.                 created=(not updated),
  41.                 update_fields=update_fields,
  42.                 raw=raw,
  43.                 using=using,
  44.             )
  45.     save_base.alters_data = True
复制代码
4、基本使用

4.1 模型保存信号
  1. from django.db.models.signals import pre_save, post_save
  2. from django.dispatch import receiver
  3. from django.contrib.auth.models import User
  4. from myapp.models import Profile
  5. @receiver(post_save, sender=User)
  6. def create_user_profile(sender, instance, created, **kwargs):
  7.     """
  8.     在用户创建时自动创建用户档案
  9.     """
  10.     if created:
  11.         Profile.objects.create(user=instance)
  12. @receiver(post_save, sender=User)
  13. def save_user_profile(sender, instance, **kwargs):
  14.     """
  15.     保存用户档案
  16.     """
  17.     instance.profile.save()
  18. @receiver(pre_save, sender=User)
  19. def pre_save_user_handler(sender, instance, **kwargs):
  20.     """
  21.     在用户保存前执行操作
  22.     """
  23.     # 例如:自动生成用户名
  24.     if not instance.username:
  25.         instance.username = f"user_{instance.email.split('@')[0]}"
复制代码
4.2 请求信号
  1. from django.core.signals import request_started, request_finished
  2. from django.dispatch import receiver
  3. @receiver(request_started)
  4. def request_started_handler(sender, environ, **kwargs):
  5.     """
  6.     请求开始时记录日志
  7.     """
  8.     import logging
  9.     logger = logging.getLogger('django.request')
  10.     logger.info(f"Request started: {environ.get('PATH_INFO')}")
  11. @receiver(request_finished)
  12. def request_finished_handler(sender, **kwargs):
  13.     """
  14.     请求结束时记录日志
  15.     """
  16.     import logging
  17.     logger = logging.getLogger('django.request')
  18.     logger.info("Request finished")
复制代码
5、自定义信号

5.1 定义信号
  1. # myapp/signals.py
  2. from django.dispatch import Signal
  3. # 定义自定义信号, providing_args, 声明信号发送时传递的参数列表, 这些参数会在信号发送时作为关键字参数传递给接收者
  4. user_registered = Signal(providing_args=["user", "request"])
  5. user_logged_in = Signal(providing_args=["user", "request"])
复制代码
5.2 发送自定义信号
  1. # myapp/views.py
  2. from django.contrib.auth import login
  3. from django.shortcuts import render, redirect
  4. from .forms import UserRegistrationForm
  5. from .signals import user_registered
  6. def register_view(request):
  7.     if request.method == 'POST':
  8.         form = UserRegistrationForm(request.POST)
  9.         if form.is_valid():
  10.             user = form.save()
  11.             
  12.             # 发送用户注册信号
  13.             user_registered.send(
  14.                 sender=user.__class__,
  15.                 user=user,
  16.                 request=request
  17.             )
  18.             
  19.             login(request, user)
  20.             return redirect('home')
  21.     else:
  22.         form = UserRegistrationForm()
  23.    
  24.     return render(request, 'registration/register.html', {'form': form})
复制代码
5.3 接收自定义信号
  1. # myapp/handlers.py
  2. from django.dispatch import receiver
  3. from django.core.mail import send_mail
  4. from django.conf import settings
  5. from .signals import user_registered
  6. @receiver(user_registered)
  7. def send_welcome_email(sender, user, request, **kwargs):
  8.     """
  9.     发送欢迎邮件给新注册用户
  10.     """
  11.     subject = 'Welcome to Our Site!'
  12.     message = f'Hello {user.username}, thank you for registering!'
  13.     from_email = settings.DEFAULT_FROM_EMAIL
  14.     recipient_list = [user.email]
  15.    
  16.     send_mail(subject, message, from_email, recipient_list)
  17. @receiver(user_registered)
  18. def create_user_profile(sender, user, request, **kwargs):
  19.     """
  20.     创建用户档案
  21.     """
  22.     from .models import UserProfile
  23.     UserProfile.objects.create(user=user)
复制代码
6、最佳实践
  1. # myapp/signals.py - 定义所有信号
  2. from django.dispatch import Signal
  3. user_registered = Signal(providing_args=["user", "request"])
  4. order_created = Signal(providing_args=["order", "user"])
  5. # myapp/handlers.py - 定义所有信号处理器
  6. from django.dispatch import receiver
  7. from .signals import user_registered, order_created
  8. @receiver(user_registered)
  9. def handle_user_registered(sender, user, request, **kwargs):
  10.     pass
  11. @receiver(order_created)
  12. def handle_order_created(sender, order, user, **kwargs):
  13.     pass
  14. # myapp/__init__.py - 导入信号处理器以确保它们被注册
  15. default_app_config = 'myapp.apps.MyAppConfig'
  16. # myapp/apps.py - 在应用配置中导入信号
  17. from django.apps import AppConfig
  18. class MyAppConfig(AppConfig):
  19.     name = 'myapp'
  20.    
  21.     def ready(self):
  22.         # 导入信号处理器
  23.         import myapp.handlers
复制代码
来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

相关推荐

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