一、简介

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,和/或修改初始 返回值。QuerySetManager

##自定义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

 

原文地址:http://www.cnblogs.com/songyunjie/p/16881478.html

1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长! 2. 分享目的仅供大家学习和交流,请务用于商业用途! 3. 如果你也有好源码或者教程,可以到用户中心发布,分享有积分奖励和额外收入! 4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解! 5. 如有链接无法下载、失效或广告,请联系管理员处理! 6. 本站资源售价只是赞助,收取费用仅维持本站的日常运营所需! 7. 如遇到加密压缩包,默认解压密码为"gltf",如遇到无法解压的请联系管理员! 8. 因为资源和程序源码均为可复制品,所以不支持任何理由的退款兑现,请斟酌后支付下载 声明:如果标题没有注明"已测试"或者"测试可用"等字样的资源源码均未经过站长测试.特别注意没有标注的源码不保证任何可用性