Django之信号量

Django之信号量

rainbowYao Lv3

Django信号(Signals)是一种允许应用组件在特定事件发生时进行通信的机制,而不必直接引用彼此。它遵循观察者模式,允许发送者(如模型实例的保存或删除)发送信号,而接收者(信号处理器)可以监听这些信号并作出响应,从而实现松耦合的组件间通信

本片笔记参考:Django信号详解


Django内置信号

模型(Model)相关的信号

pre_init:在Django模型实例化其构造方法之前触发

post_init:在Django模型实例化其构造方法之后触发

pre_save:在Django模型对象保存到数据库之前触发

post_save:在Django模型对象保存到数据库之后触发,无论对象是新建还是更新

pre_delete:在Django模型对象从数据库中删除之前触发

post_delete:在Django模型对象从数据库中删除之后触发

m2m_changed:在使用Many-to-Many字段进行添加、移除或清空操作前后触发

class_prepared:当Django发现并准备使用已注册应用中的模型类时触发


数据库迁移相关的信号

pre_migrate:执行migrate命令前触发

post_migrate:执行migrate命令后触发


请求和响应相关的信号

request_startedrequest_finished:分别在Django开始处理HTTP请求和完成请求处理后触发

got_request_exception:当视图函数中抛出异常时触发


测试相关的信号

Test signals:在测试运行的不同阶段触发,用于测试环境的设置或清理等


其他信号

setting_changed:当Django设置在运行时被更改时触发

template_rendered:模板渲染完成后触发,可用于模板调试


使用场景

  1. 日志记录:在模型保存或删除时自动记录日志
  2. 通知系统:用户操作后发送邮件或短信通知
  3. 权限检查:在模型保存前检查用户是否有相应权限
  4. 数据同步:数据库更改后同步到搜索引擎或缓存系统
  5. 清理任务:删除模型实例时,同步清理关联的文件或资源
  6. 异步处理:在视图操作后触发异步任务,如发送邮件、处理图像等

使用示例

简单语法

假设你有一个 BookAuthor 模型,它们之间是多对多的关系:

1
2
3
4
5
6
7
8
9
10
from django.db import models
from django.db.models.signals import m2m_changed
from django.dispatch import receiver

class Author(models.Model):
name = models.CharField(max_length=100)

class Book(models.Model):
title = models.CharField(max_length=100)
authors = models.ManyToManyField(Author)

你可以使用 m2m_changed 信号来监听 Book 模型中的 authors 字段。当你向书籍添加或移除作者时,信号会被触发

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@receiver(m2m_changed, sender=Book.authors.through)
def authors_changed(sender, instance, action, **kwargs):
if action == 'pre_add':
print(f"即将向书籍 '{instance.title}' 添加作者。")
elif action == 'post_add':
print(f"已向书籍 '{instance.title}' 添加作者。")
elif action == 'pre_remove':
print(f"即将从书籍 '{instance.title}' 移除作者。")
elif action == 'post_remove':
print(f"已从书籍 '{instance.title}' 移除作者。")
elif action == 'pre_clear':
print(f"即将清空书籍 '{instance.title}' 的所有作者。")
elif action == 'post_clear':
print(f"已清空书籍 '{instance.title}' 的所有作者。")

在这个示例中,我们定义了一个信号处理函数 authors_changed,并将其与 m2m_changed 信号连接。当向 Book模型的 authors 字段添加、移除或清空作者时,系统会根据具体操作类型输出相应的消息


其中有一些语法成文做简单的分析:

  1. @receiver 装饰器

@receiver 是 Django 提供的一个装饰器,用于将一个信号与某个处理函数(在这里是 authors_changed)关联起来。当信号触发时,Django 会自动调用该处理函数

  • 作用:将特定的信号与函数绑定,使得当信号触发时,函数能够自动执行
  • 用法@receiver(signal_name, sender=SenderClass)

  1. sender

sender 是装饰器中的一个参数,用于指定发出信号的模型类或中间表。在这个例子中,sender 参数设置为 Book.authors.through,即 Book 模型与 Author 模型之间的 Many-to-Many 中间表

  • 作用:指定信号来自哪个模型或中间表。通过这一点,Django 可以确定什么时候应该触发该信号
  • 用法sender=Book.authors.through 指定了当 Book 模型的 authors 字段发生变化时,信号会触发

  1. action 参数

action 是信号处理函数的一个参数,表示当前发生的操作类型。Django 会在触发信号时自动将操作类型传递给该参数

  • 常见的取值

    • 'pre_add':在添加关系之前触发
    • 'post_add':在添加关系之后触发
    • 'pre_remove':在移除关系之前触发
    • 'post_remove':在移除关系之后触发
    • 'pre_clear':在清空关系之前触发
    • 'post_clear':在清空关系之后触发
  • 作用:通过检查 action 的值,可以确定当前操作的具体类型,从而执行相应的逻辑


  1. instance 参数

instance 参数表示触发信号的模型实例。在本例中,它是 Book 模型的一个实例

  • 作用:提供触发信号的具体对象,允许处理函数访问和修改这个对象的状态

  1. kwargs 参数

kwargs 是一个字典,包含了与信号相关的其他参数和数据

  • 作用:提供灵活性,允许处理函数根据需要访问更多信息

  1. authors_changed 函数的功能

authors_changed 函数根据 action 的不同值,打印出不同的消息,用于调试或监控在 Book 模型的 authors 字段上进行的操作。通过这个函数,你可以在特定的操作(如添加、移除、清空)前后执行额外的逻辑,比如更新日志、通知用户等


  1. 这个函数应该写在哪个文件里

通常,信号处理函数和信号的连接逻辑应该写在 signals.py 文件中

为了确保信号处理函数生效,你需要在应用的 apps.py 文件中进行导入

1
2
3
4
5
6
7
8
# apps.py
from django.apps import AppConfig

class YourAppConfig(AppConfig):
name = 'your_app_name'

def ready(self):
import your_app_name.signals

通过这种方式,你的信号处理函数会在 Django 启动时自动连接到相应的信号


示例

假设我们有一个如下User模型,我们希望每次有User对象新创建时都打印出有新用户注册的提示信息,我们可以使用Django信号(signals)轻松实现。我们的信号发送者sender是User模型,每当User模型执行post_save动作时就会发出信号。此时我们自定义的create_user函数,一旦监听到User发出的post_save信号就会执行,先通过if created判断对象是新创建的还是被更新的;如果对象是新创建的,就会打印出提示信息

1
2
3
4
5
6
7
8
9
10
11
12
13
from django.db import models
from django.db.models.signals import post_save
from django.dispatch import receiver

class User(models.Model):
name = models.CharField(max_length=16)
gender = models.CharField(max_length=32, blank=True)

def create_user(sender, instance, created, **kwargs):
if created:
print("New user created!")

post_save.connect(create_user, sender=User)

在上例中我们使用了信号(post_save)自带的connect的方法将自定义的函数与信号发出者User模型进行了连接


在实际应用中一个更常用的方式是使用@receiver装饰器实现发送者与监听函数的连接,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
from django.db import models
from django.db.models.signals import post_save
from django.dispatch import receiver

class User(models.Model):
name = models.CharField(max_length=16)
gender = models.CharField(max_length=32, blank=True)

@receiver(post_save, sender=User)
def create_user(sender, instance, created, **kwargs):
if created:
print("New user created!")

@receiver(post_save, sender=User)读起来的意思就是监听User模型发出的post_save信号


在Django中,request_started,request_finished, 和 got_request_exception 这三个信号可以用于在请求处理的不同阶段执行特定的代码逻辑,例如日志记录、性能监控、资源管理等。以下是使用这些信号的基本步骤

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#your_app/single.py
from django.core.signals import request_started, request_finished, got_request_exception
from django.dispatch import receiver

@receiver(request_started)
def on_request_started(sender, environ, **kwargs):
# 在这里添加处理逻辑,比如开始记录请求时间、初始化日志上下文等
print("Request started.")

@receiver(request_finished)
def on_request_finished(sender, **kwargs):
# 在这里添加处理逻辑,比如记录请求结束时间、计算请求耗时、清理资源等
print("Request finished.")

@receiver(got_request_exception)
def on_request_exception(sender, request, **kwargs):
# 在这里添加处理逻辑,比如记录异常信息、发送报警等
print("Exception occurred during request processing:", request)

注册信号处理器:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# your_app/apps.py
from django.apps import AppConfig
from django.core.signals import request_started, request_finished, got_request_exception
from .signals import on_request_started, on_request_finished, on_request_exception

class YourAppConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'your_app'

def ready(self):
# 注册信号处理器
request_started.connect(on_request_started)
request_finished.connect(on_request_finished)
got_request_exception.connect(on_request_exception)

下面介绍自定义信号,首先,你需要定义一个新的信号。这通常在应用的一个单独的signals.py文件中完成。信号通过django.dispatch.Signal类来定义,并可以指定信号发送时需要携带的参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#创建一个名为APP01的应用
# APP01/signals.py
from django.dispatch import Signal
from django.dispatch import receiver

# 定义一个自定义信号,提供额外的参数example_arg
my_custom_signal = Signal(providing_args=['example_arg'])


#注册信号回调函数
@receiver(my_custom_signal)
def my_signal_handler(sender, **kwargs):
example_arg = kwargs.get('example_arg', "No argument provided.")
print(f"Custom signal received with argument: {example_arg}")
# 在这里执行你的逻辑,比如记录日志、更新数据库、发送邮件等

然后注册信号处理器,你可以在应用的apps.py文件的ready()方法中注册这些信号处理器,确保它们在Django启动时被正确注册

1
2
3
4
5
6
7
8
9
10
11
# APP01/apps.py
from django.apps import AppConfig
from .signals import my_signal_handler

class YourAppConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'your_app'

def ready(self):
# 连接信号处理器
my_custom_signal.connect(my_signal_handler)

下面,例如在某个视图中发送信号

1
2
3
4
5
6
7
8
from django.http import HttpResponse
from .signals import my_custom_signal

def some_view(request):
# 执行某些操作...
# 例如当用户成功登录的时候....
my_custom_signal.send(sender=None, example_arg="This is an example argument.")
return HttpResponse("Signal sent.")
  • 标题: Django之信号量
  • 作者: rainbowYao
  • 创建于 : 2024-08-23 21:37:46
  • 更新于 : 2024-09-18 09:21:39
  • 链接: https://redefine.ohevan.com/2024/08/23/Django之信号量/
  • 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。
此页目录
Django之信号量