Django如何在类上使用``receiver``装饰器而不是函数

MrK*_*tts 6 python django

使用Django信号receiver装饰器我有以下功能.

@receiver(post_save)
def action_signal(sender, instance, created, **kwargs):
    pass
Run Code Online (Sandbox Code Playgroud)

是否可以receiver在类上使用装饰器而不是函数?原因是我想要一个__init__方法等.

即我怎样才能得到这样的东西......

class ActionSignals

    def __init__(self):
        self.stuff

    @receiver(post_save)
    def action_signal(sender, instance, created, **kwargs):
        print(self.stuff)
Run Code Online (Sandbox Code Playgroud)

Iva*_*van 7

添加基于类的信号处理程序

你可以这样做:

class ActionSignals(object):
    def __init__(self, *args, **kwargs):
        # ...            

    def __call__(self, *args, **kwargs):
        print(self.stuff)
Run Code Online (Sandbox Code Playgroud)

然后连接信号处理程序:

handler = ActionSignals()

post_save.connect(handler)
Run Code Online (Sandbox Code Playgroud)

这利用了 python 的“神奇”__call__方法,允许您将类的实例用作函数。

避免重复

请注意在代码中添加处理程序的位置,因为您可能会创建重复项。例如,如果您要将第二位代码放在模块根目录中,则每次导入模块时都会添加一个处理程序。

为了避免这种情况,您可以执行以下操作

post_save.connect(handler, dispatch_uid="my_unique_identifier")
Run Code Online (Sandbox Code Playgroud)

正如 @Alasdair 指出的,您可以添加处理程序AppConfig.ready()(这是推荐的位置),尽管通常可以在任何地方完成,如果您注意不要创建不需要的重复项。

请参阅“此代码应该放在哪里?” 在本文档中

  • 毕竟这对于 @AdamParkin 来说可能毫无用处,但也许它会对其他人有所帮助:从查看代码来看,您似乎可以将相同的参数传递给装饰器,因为它会将它们原封不动地传递给 signal.connect() 。查看`django/dispatch/dispatcher.py`,行[288](https://github.com/django/django/blob/f425835cbec603888ffb461ec0f54ef191ace849/django/dispatch/dispatcher.py#L288)和[290](https: //github.com/django/django/blob/f425835cbec603888ffb461ec0f54ef191ace849/django/dispatch/dispatcher.py#L290)(对于Django 3.0)。 (4认同)
  • 如果使用“@receiver”装饰器,如何指定“dispatch_uid”? (3认同)

Ala*_*air 6

receiver在类方法上使用装饰器并没有多大意义.你什么时候期望实例化对象和__init__运行方法?

您可以使用手动方法连接信号,而不是receiver装饰器.

首先实例化对象:

action_signal = ActionSignals()
Run Code Online (Sandbox Code Playgroud)

然后你可以使用该connect方法连接信号:

post_save.connect(action_signal.action_signal)
Run Code Online (Sandbox Code Playgroud)

  • 是的,初始化对象并在app config的`ready()`方法中连接信号听起来不错. (2认同)