传递参数django信号 - post_save/pre_save

Mo *_*abi 20 django signals argument-passing

我正在研究Django 1.6中的通知应用程序,我想将其他参数传递给Django信号,例如post_save.我尝试使用functools的部分但没有运气.

from functools import partial
post_save.connect(
    receiver=partial(notify,
        fragment_name="categories_index"),
            sender=nt.get_model(),
            dispatch_uid=nt.sender
    )
Run Code Online (Sandbox Code Playgroud)

notifyfunction有一个关键字参数fragment_name,我希望在我的信号中默认传递.

有什么建议?

Eug*_*tov 22

您可以在模型的自定义保存方法中定义其他参数,如下所示:

class MyModel(models.Model):
    ....

    def save(self, *args, **kwargs):
        super(MyModel, self).save(*args, **kwargs)
        self.my_extra_param = 'hello world'
Run Code Online (Sandbox Code Playgroud)

并通过post_save信号接收器中的实例访问此附加参数:

@receiver(post_save, sender=MyModel)
def process_my_param(sender, instance, *args, **kwargs):
    my_extra_param = instance.my_extra_param
Run Code Online (Sandbox Code Playgroud)

  • 这个答案是错误的。如果它要工作,那么需要在调用 super save 方法之前执行 self.my_extra_param = 'hello world'` 。 (5认同)

Dan*_*cci 13

您使用partial的尝试不起作用,因为默认情况下这些接收器使用弱引用连接.

根据Django文档:

Django默认将信号处理程序存储为弱引用,因此如果您的处理程序是本地函数,则可能是垃圾回收.要防止这种情况,请在调用信号的connect()时传递weak = False.

from functools import partial
post_save.connect(
    receiver=partial(notify,
        fragment_name="categories_index"),
            sender=nt.get_model(),
            dispatch_uid=nt.sender,
            weak=False
    )
Run Code Online (Sandbox Code Playgroud)

包括weak = False,这部分不会被垃圾收集.

我的原始答案如下,并采取了一种不使用偏的方法.

在将其与post_save接收器连接之前,您可以修饰保存后的功能.

from django.dispatch import receiver
from django.db.models.signals import pre_save, post_save, post_delete

def extra_args(fragment_name, *args, **kwargs):
    def inner1(f, *args, **kwargs):
        def inner2(sender, instance, **kwargs):
            f(sender, instance, fragment_name=fragment_name, **kwargs)
        return inner2
    return inner1

@receiver(post_save, sender=ExampleModel)
@extra_args(fragment_name="categories_index")
def my_post_save(sender, instance, fragment_name, **kwargs):
    print "fragment_name : ", fragment_name
    #rest of post save...
Run Code Online (Sandbox Code Playgroud)

extra_args中的额外内部是用于获取参数的装饰器.

如果您想以编程方式执行此操作,则此方法的工作方式相同,但请注意,您需要包含weak=False以使包装函数不被垃圾回收.

receiver(post_save, sender=aSenderClass, weak=False)(extra_args(fragment_name="meep")(my_post_save))
Run Code Online (Sandbox Code Playgroud)

或者没有包装,但调用post_save.connect就像你原来的尝试一样

post_save.connect(extra_args(fragment_name="meepConnect")(my_post_save), sender=Author, weak=False)
Run Code Online (Sandbox Code Playgroud)


Yok*_*hen 11

我尝试了尤金·索尔达托夫的答案,但这让我意识到它可以简单得多:

你可以有类似的东西:

obj = MyModel.objects.first()
obj.my_extra_param = "hello world"
obj.save() # this will trigger the signal call
Run Code Online (Sandbox Code Playgroud)

然后像尤金的回答那样拥有接收器,它仍然可以工作。

@receiver(post_save, sender=MyModel)
def process_my_param(sender, instance, *args, **kwargs):
    my_extra_param = instance.my_extra_param
Run Code Online (Sandbox Code Playgroud)

无需创建容易出现错误的自定义保存方法。

这就是它目前在 Django 3.0 中的工作方式。我没有尝试过以前的版本。

为什么会出现这种情况?好的文档可以为您提供答案: https://docs.djangoproject.com/en/3.2/ref/models/instances/#what-happens-when-you-save

  • 因为它只是写在对象上,而不是写在数据库层面上。与任何 Python 对象一样,它能够保存新定义的字段并传递这些数据。每当你执行 `obj.refresh_from_db()` 时,该值就会消失。 (2认同)