炳裘垦 发表于 2025-9-30 11:44:10

Django信号

1、基本概念

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

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

2.1 模型信号

from django.db.models.signals import (
    pre_init, post_init,          # 模型实例化前后
    pre_save, post_save,          # 模型保存前后
    pre_delete, post_delete,      # 模型删除前后
    m2m_changed,                  # 多对多关系变更
    class_prepared                # 模型类准备就绪
)2.2 请求/响应信号

from django.core.signals import (
    request_started,            # 请求开始
    request_finished,             # 请求结束
    got_request_exception         # 请求异常
)2.3 数据库包装器信号

from django.db.backends.signals import (
    connection_created            # 数据库连接创建
)3、源码分析

3.1 Signal简化版代码

class Signal:

    def __init__(self, use_caching=False):
      # 接收者列表
      self.receivers = []
      # 是否使用缓存
      self.use_caching = use_caching
      # 用于优化的缓存
      self.sender_receivers_cache = weakref.WeakKeyDictionary() if use_caching else {}

    def connect(self, receiver, sender=None, weak=True, dispatch_uid=None):
      """注册接收器"""
      # 获取或创建dispatch_uid
      if dispatch_uid:
            lookup_key = (dispatch_uid, _make_id(sender))
      else:
            lookup_key = (_make_id(receiver), _make_id(sender))

      # 如果是弱引用,使用weakref
      if weak:
            ref = weakref.ref
            receiver_object = receiver
            if hasattr(receiver, "__self__") and hasattr(receiver, "__func__"):
                ref = weakref.WeakMethod
                receiver_object = receiver.__self__
            receiver = ref(receiver)
            weakref.finalize(receiver_object, self._remove_receiver)

      # 将接收器添加到列表
      self.receivers.append((lookup_key, receiver, is_async))

    def disconnect(self, receiver=None, sender=None, dispatch_uid=None):
      """
      断开接收者与信号的连接, 清理缓存和self.receivers
      """
      if dispatch_uid:
            lookup_key = (dispatch_uid, _make_id(sender))
      else:
            lookup_key = (_make_id(receiver), _make_id(sender))

      disconnected = False
      with self.lock:
            self._clear_dead_receivers()
            for index in range(len(self.receivers)):
                r_key, *_ = self.receivers
                if r_key == lookup_key:
                  disconnected = True
                  del self.receivers
                  break
            self.sender_receivers_cache.clear()
      return disconnected

    def has_listeners(self, sender=None):
      sync_receivers, async_receivers = self._live_receivers(sender)
      return bool(sync_receivers) or bool(async_receivers)

    def send(self, sender, **named):
      """
      发送信号给所有连接的接收者
      """
      sync_receivers = self._live_receivers(sender)
      for receiver in sync_receivers:
            # 调用接收器
            response = receiver(signal=self, sender=sender, **named)
            responses.append((receiver, response))
      return responses

   

    def send_robust(self, sender, **named):
      """
      发送信号,即使某些接收者抛出异常也继续执行
      """
      pass


    def _live_receivers(self, sender):
      """
      获取活动的接收者
      """
      pass3.2 调用流程

class ModelBase(type):
    """Model的元类,控制Model类的创建"""

    def __new__(cls, name, bases, attrs, **kwargs):
      pass

      
    def _prepare(cls):
      # ...
      # 模型类准备就绪
      class_prepared.send(sender=cls)

class Model(AltersData, metaclass=ModelBase):
    def __init__(self, *args, **kwargs):
      # 模型实例化前
      pre_init.send(sender=cls, args=args, kwargs=kwargs)
      # ...
      # 模型实例化后
      post_init.send(sender=cls, instance=self)

    def save_base(
      self,
      raw=False,
      force_insert=False,
      force_update=False,
      using=None,
      update_fields=None,
    ):

      if not meta.auto_created:
            # 模型保存前
            pre_save.send(
                sender=origin,
                instance=self,
                raw=raw,
                using=using,
                update_fields=update_fields,
            )
      # ...
      if not meta.auto_created:
            # 模型保存后
            post_save.send(
                sender=origin,
                instance=self,
                created=(not updated),
                update_fields=update_fields,
                raw=raw,
                using=using,
            )

    save_base.alters_data = True4、基本使用

4.1 模型保存信号

from django.db.models.signals import pre_save, post_save
from django.dispatch import receiver
from django.contrib.auth.models import User
from myapp.models import Profile

@receiver(post_save, sender=User)
def create_user_profile(sender, instance, created, **kwargs):
    """
    在用户创建时自动创建用户档案
    """
    if created:
      Profile.objects.create(user=instance)

@receiver(post_save, sender=User)
def save_user_profile(sender, instance, **kwargs):
    """
    保存用户档案
    """
    instance.profile.save()

@receiver(pre_save, sender=User)
def pre_save_user_handler(sender, instance, **kwargs):
    """
    在用户保存前执行操作
    """
    # 例如:自动生成用户名
    if not instance.username:
      instance.username = f"user_{instance.email.split('@')}"4.2 请求信号

from django.core.signals import request_started, request_finished
from django.dispatch import receiver

@receiver(request_started)
def request_started_handler(sender, environ, **kwargs):
    """
    请求开始时记录日志
    """
    import logging
    logger = logging.getLogger('django.request')
    logger.info(f"Request started: {environ.get('PATH_INFO')}")

@receiver(request_finished)
def request_finished_handler(sender, **kwargs):
    """
    请求结束时记录日志
    """
    import logging
    logger = logging.getLogger('django.request')
    logger.info("Request finished")5、自定义信号

5.1 定义信号

# myapp/signals.py
from django.dispatch import Signal

# 定义自定义信号, providing_args, 声明信号发送时传递的参数列表, 这些参数会在信号发送时作为关键字参数传递给接收者
user_registered = Signal(providing_args=["user", "request"])
user_logged_in = Signal(providing_args=["user", "request"])5.2 发送自定义信号

# myapp/views.py
from django.contrib.auth import login
from django.shortcuts import render, redirect
from .forms import UserRegistrationForm
from .signals import user_registered

def register_view(request):
    if request.method == 'POST':
      form = UserRegistrationForm(request.POST)
      if form.is_valid():
            user = form.save()
            
            # 发送用户注册信号
            user_registered.send(
                sender=user.__class__,
                user=user,
                request=request
            )
            
            login(request, user)
            return redirect('home')
    else:
      form = UserRegistrationForm()
   
    return render(request, 'registration/register.html', {'form': form})5.3 接收自定义信号

# myapp/handlers.py
from django.dispatch import receiver
from django.core.mail import send_mail
from django.conf import settings
from .signals import user_registered

@receiver(user_registered)
def send_welcome_email(sender, user, request, **kwargs):
    """
    发送欢迎邮件给新注册用户
    """
    subject = 'Welcome to Our Site!'
    message = f'Hello {user.username}, thank you for registering!'
    from_email = settings.DEFAULT_FROM_EMAIL
    recipient_list =
   
    send_mail(subject, message, from_email, recipient_list)

@receiver(user_registered)
def create_user_profile(sender, user, request, **kwargs):
    """
    创建用户档案
    """
    from .models import UserProfile
    UserProfile.objects.create(user=user)6、最佳实践

# myapp/signals.py - 定义所有信号
from django.dispatch import Signal

user_registered = Signal(providing_args=["user", "request"])
order_created = Signal(providing_args=["order", "user"])

# myapp/handlers.py - 定义所有信号处理器
from django.dispatch import receiver
from .signals import user_registered, order_created

@receiver(user_registered)
def handle_user_registered(sender, user, request, **kwargs):
    pass

@receiver(order_created)
def handle_order_created(sender, order, user, **kwargs):
    pass

# myapp/__init__.py - 导入信号处理器以确保它们被注册
default_app_config = 'myapp.apps.MyAppConfig'

# myapp/apps.py - 在应用配置中导入信号
from django.apps import AppConfig

class MyAppConfig(AppConfig):
    name = 'myapp'
   
    def ready(self):
      # 导入信号处理器
      import myapp.handlers
来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

嘀荼酴 发表于 3 天前

过来提前占个楼
页: [1]
查看完整版本: Django信号