一、简介
Django 包含一个“信号调度器”,当框架中其他地方发生动作时,它可以帮助解耦的应用程序得到通知。简而言之,信号允许某些发送者通知一组接收者某些动作已经发生。信号系统包含的三要素:
1.发送者:信号的发出放
2.信号:信号本身
3.接收者:信号的接收者
二、信号定义
所有信号都是django.dispatch.Signal
实例。
#声明pizza_done信号 import django.dispatch pizza_done = django.dispatch.Signal()
三、信号发送
在django中有两种发送信号得方式
Signal.send(sender, **kwargs) Signal.send_robust(sender, **kwargs)
要发送信号,请调用Signal.send()
(所有内置信号都使用它)或Signal.send_robust()
. 必须提供sender
参数(大多数情况下是一个类),并且可以提供任意数量的其他关键字参数。
class PizzaStore: ... def send_pizza(self, toppings, size): pizza_done.send(sender=self.__class__, toppings=toppings, size=size) ...
send() 和 send_robust() 都返回一组元组对 [(receiver, response), ... ] 的列表,表示被调用的接收函数及其响应值的列表。不同的是处理异常的方式,
send()
不捕获接收者提出的任何异常;它只是允许错误传播。因此,并非所有接收器都可以在遇到错误时被通知信号。send_robust()
捕获从 PythonException
类派生的所有错误,并确保所有接收者都收到信号通知。如果发生错误,则在引发错误的接收者的元组对中返回错误实例。
四、收听信号
要接收信号,使用接收器函数。Signal.connect()
发送信号时调用接收器函数。一次调用所有信号的接收器函数,按照它们注册的顺序。
Signal.connect(receiver, sender=None, weak=True, dispatch_uid=None) #receiver - 将连接到此信号的回调函数 #sender – 指定一个特定的发送者来接收信号 #weak – Django 默认将信号处理程序存储为弱引用。因此,如果您的接收器是本地函数,它可能会被垃圾回收。为防止这种情况,weak=False请在调用信号的connect()方法时传递。 #dispatch_uid – 在可能发送重复信号的情况下,信号接收器的唯一标识符。有关详细信息,请参阅 防止重复信号。
4.1、接收函数
接收者可以是任何 Python 函数或方法
def my_callback(sender, **kwargs): print("Request finished!")
该函数接受一个sender
参数,以及通配符关键字参数 ( **kwargs
);所有信号处理程序都必须采用这些参数。
4.2、连接接收器功能
两种方法可以将接收器连接到信号。
#1、手动连接 from django.core.signals import request_finished request_finished.connect(my_callback) #2、装饰器receiver连接 #receiver(signal, **kwargs) #signal,一个信号或信号列表来连接一个函数 #**kwargs要传递给 函数的通配符关键字参数 from django.core.signals import request_finished from django.dispatch import receiver @receiver(request_finished) def my_callback(sender, **kwargs): print("Request finished!")
4.3、连接指定发送者发送的信号
有些信号会被多次发送,但可能只会对接收这些信号的某个子集感兴趣。例如,考虑在 django.db.models.signals.pre_save
模型保存之前发送的信号。大多数情况下,不需要知道何时保存了任何模型——只需保存一个特定模型即可。
在这些情况下,可以注册以接收仅由特定发送者发送的信号。在django.db.models.signals.pre_save
这种的情况下,发送者将是正在保存的模型类,因此可以表明只想要某个模型发送的信号:
#my_handler只有在保存的实例时才会调用该函数MyModel 。 #不同的信号使用不同的对象作为发送者 from django.db.models.signals import pre_save from django.dispatch import receiver from myapp.models import MyModel @receiver(pre_save, sender=MyModel) def my_handler(sender, **kwargs): ...
4.4、防止信号重复
在某些情况下,将接收器连接到信号的代码可能会运行多次。这可能会导致接收器函数被多次注册,从而为信号事件调用多次,此时应传递唯一标识符作为dispatch_uid
参数来标识您的接收器函数。这个标识符通常是一个字符串,尽管任何可散列的对象都足够了。dispatch_uid
最终结果是,对于每个唯一值,您的接收器函数只会绑定到信号一次:
from django.core.signals import request_finished request_finished.connect(my_callback, dispatch_uid="my_unique_identifier")
4.5、断开信号
Signal.disconnect(receiver=None, sender=None, dispatch_uid=None) #receiver参数指示注册的接收器断开连接
五、内置信号
1.模型信号
信号会使代码变得难以维护,所以必须实现一个辅助方法,方便更新模型并执行外的逻辑,或者使用模型信号之前的覆盖模型方法。
自定义管理器
可以通过扩展基类并在模型中实例化自定义Manager
来在特定模型 中使用自定义。
您可能想要自定义 a 有两个原因Manager
:添加额外的 方法Manager
,和/或修改初始 返回值。QuerySet
Manager
##自定义Manager添加了一个方法with_counts()用于获取 附加了额外 属性QuerySet的对象 from django.db import models from django.db.models.functions import Coalesce class PollManager(models.Manager): def with_counts(self): return self.annotate( num_responses=Coalesce(models.Count("response"), 0) ) class OpinionPoll(models.Model): question = models.CharField(max_length=200) objects = PollManager() class Response(models.Model): poll = models.ForeignKey(OpinionPoll, on_delete=models.CASCADE) # ...
覆盖模型
比如使用此模型
from django.db import models class Book(models.Model): title = models.CharField(max_length=100) author = models.CharField(max_length=50)
查询时Book.objects.all()
将返回数据库中的所有书籍,通过覆盖方法来覆盖manager的基础,返回想要的属性
#Book.objects.all()仅返回 Roald Dahl 编写的书籍。 class DahlBookManager(models.Manager): def get_queryset(self): return super().get_queryset().filter(author='Roald Dahl') # Then hook it into the Book model explicitly. class Book(models.Model): title = models.CharField(max_length=100) author = models.CharField(max_length=50) objects = models.Manager() # The default manager. dahl_objects = DahlBookManager() # The Dahl-specific manager.
2、模型信号
2.1、pre_init
django.db.models.signals.pre_init #每当您实例化 Django 模型时,都会在模型方法的开头发送此信号__init__()。 #使用此信号发送的参数: sender #刚刚创建实例的模型类。 args #传递给的位置参数列表__init__()。 kwargs #传递给的关键字参数字典__init__()。
2.2、post_init
django.db.models.signals.post_init #在__init__()方法完成时发送的 #使用此信号发送的参数: sender #如上:刚刚创建实例的模型类。 instance #刚刚创建的模型的实际实例。
2.3、pre_save
django.db.models.signals.pre_save #这是在模型方法的开头发送的save() 。 #使用此信号发送的参数: sender #模型类。 instance #正在保存的实际实例。 raw #一个布尔值;True如果模型完全按照呈现方式保存(即在加载夹具时)。不应查询/修改数据库中的其他记录,因为数据库可能尚未处于一致状态。 using #正在使用的数据库别名。 update_fields #Model.save()传递给或未传递给 时要None 更新的字段集。
2.4、post_save
django.db.models.signals.post_save #方法结束时发送 save 使用此信号发送的参数: sender #模型类。 instance #正在保存的实际实例。 created #一个布尔值;True如果创建了新记录。 raw #一个布尔值;True如果模型完全按照呈现方式保存(即在加载夹具时)。不应查询/修改数据库中的其他记录,因为数据库可能尚未处于一致状态。 using #正在使用的数据库别名。 update_fields #Model.save()传递给或未传递给 时要None 更新的字段集
2.5、pre_delete
jango.db.models.signals.pre_delete¶ #delete() 在模型方法和查询集方法的开头发送delete()。 #使用此信号发送的参数: sender #模型类。 instance #被删除的实际实例。 using #正在使用的数据库别名。
2.6、post_delete
django.db.models.signals.post_delete #在模型方法和 查询集方法pre_delete的末尾发送 。 #使用此信号发送的参数: sender #模型类。 instance #被删除的实际实例。 #请注意,该对象将不再存在于数据库中,因此请务必小心处理此实例。 using #正在使用的数据库别名。
2.7、m2m_changed
django.db.models.signals.m2m_changed #ManyToManyField在模型实例上更改 a时发送。严格来说,这不是模型信号,因为 ManyToManyField它 是 由pre_savepost_savepre_deletepost_delete #使用此信号发送的参数: sender #描述 ManyToManyField. 这个类是在定义多对多字段时自动创建的;可以使用 through多对多字段上的属性访问它。 instance #更新多对多关系的实例。这可以是 的实例,也可以是与之相关sender的类的 实例。ManyToManyField action #一个字符串,指示对关系执行的更新类型。这可以是以下之一: "pre_add" #在将一个或多个对象添加到关系之前发送。 "post_add" #在将一个或多个对象添加到关系后发送。 "pre_remove" #在从关系中删除一个或多个对象之前发送。 "post_remove" #在从关系中删除一个或多个对象后发送。 "pre_clear" #在关系清除之前发送。 "post_clear" #关系清除后发送。 reverse #指示关系的哪一侧被更新(即,正在修改的是正向还是反向关系)。 model #添加到关系、从关系中删除或从关系中清除的对象的类。 pk_set #对于pre_addandpost_add操作,这是一组将要或已经添加到关系中的主键值。这可能是提交要添加的值的子集,因为插入必须过滤现有值以避免数据库IntegrityError。 using #正在使用的数据库别名。
3、管理信号
3.1pre_migrate
django.db.models.signals.pre_migrate¶ #migrate在开始安装应用程序之前由命令发送。models它不会为缺少模块的应用程序发出。 #使用此信号发送的参数: sender #AppConfig即将迁移/同步的应用程序实例。 app_config #与 相同sender。 verbosity #manage.py指示在屏幕上打印了多少信息 #监听的函数pre_migrate应该根据这个参数的值调整它们输出到屏幕的内容。 interactive #如果interactive是True,则提示用户在命令行输入内容是安全的。如果interactive是False,则侦听此信号的函数不应尝试提示任何内容。 stdout #应重定向详细输出的类流对象 using #命令将在其上操作的数据库的别名。 plan #将用于迁移运行的迁移计划。虽然该计划不是公共 API,但这允许在极少数情况下需要了解该计划。计划是双元组的列表,第一项是迁移类的实例,第二项显示迁移是回滚 ( True) 还是应用 ( False)。 apps #Apps包含迁移运行前项目状态的实例。应该使用它而不是全局 apps注册表来检索要对其执行操作的模型
3.2、post_migrate
django.db.models.signals.post_migrate #在migrate(即使没有运行迁移)和 flush命令结束时发送。models它不会为缺少模块的应用程序发出 。 #此信号的处理程序不得执行数据库模式更改,因为这样做可能会导致flush命令在命令期间运行时失败 migrate。 #使用此信号发送的参数: sender #AppConfig刚刚安装的应用程序的实例。 app_config #与 相同sender。 verbosity #manage.py指示在屏幕上打印了多少信息。有关详细信息,请参阅--verbosity标志。 #监听的函数post_migrate应该根据这个参数的值调整它们输出到屏幕的内容。 interactive #如果interactive是True,则提示用户在命令行输入内容是安全的。如果interactive是False,则侦听此信号的函数不应尝试提示任何内容。 #例如,应用只在isdjango.contrib.auth时提示创建超级用户。interactiveTrue stdout #应重定向详细输出的类流对象。 using #用于同步的数据库别名。默认为default 数据库。 plan #用于迁移运行的迁移计划。虽然该计划不是公共 API,但这允许在极少数情况下需要了解该计划。计划是双元组的列表,第一项是迁移类的实例,第二项显示迁移是回滚 ( True) 还是应用 ( False)。 apps #Apps包含迁移运行后项目状态的实例。应该使用它而不是全局 apps注册表来检索要对其执行操作的模型。
4、请求会响应信号
核心框架在处理请求时发送的信号,使用信号回事代码维护艰难,要考虑配合中间件去使用
4.1、request_started
django.core.signals.request_started¶ #当 Django 开始处理 HTTP 请求时发送。 #使用此信号发送的参数: sender #处理请求的处理程序类——例如django.core.handlers.wsgi.WsgiHandler——。 environ #environ提供给请求的字典。
4.2、request_finished
django.core.signals.request_finished¶ #当 Django 完成向客户端发送 HTTP 响应时发送。 #使用此信号发送的参数: sender #处理程序类
4.3、got_request_exception
django.core.signals.got_request_exception #每当 Django 在处理传入的 HTTP 请求时遇到异常时,都会发送此信号。 #使用此信号发送的参数: sender #未使用(总是None)。 request #HttpRequest对象。
以上内容均来自django4.1版本内容,具体信息查看
1、https://docs.djangoproject.com/en/4.1/ref/signals/#pre-init
2、https://docs.djangoproject.com/en/4.1/ref/signals/
3、https://docs.djangoproject.com/en/4.1/topics/signals/#receiver-functions
标签:signals,sender,models,django,发送,信号 From: https://www.cnblogs.com/songyunjie/p/16881478.html