django传递参数-post_save / pre_save

问题描述 投票:22回答:5

我正在Django 1.6中开发一个通知应用程序,我想将其他参数传递给Django信号,例如post_save。我尝试使用functools中的partial,但是没有运气。

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

[notify函数具有关键字参数fragment_name,我想在信号中将其作为默认值传递。

有什么建议吗?

django signals argument-passing
5个回答
13
投票

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

根据Django docs

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
    )

包括weak = False,并且不会部分收集此部分。

我的原始答案在下面,并且采用了不使用局部方法。

您可以在将post save功能与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...

extra_args中的额外内部用于decorators that take parameters

如果您要以编程方式执行此操作,则其工作方式相同,但请注意,您需要包含weak=False才能使包装的功能不被垃圾收集。

receiver(post_save, sender=aSenderClass, weak=False)(extra_args(fragment_name="meep")(my_post_save))

或者不进行换行,而是像原始尝试那样对post_save.connect进行部分调用

post_save.connect(extra_args(fragment_name="meepConnect")(my_post_save), sender=Author, weak=False)

23
投票

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

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

    def save(self, *args, **kwargs):
        super(MyModel, self).save(*args, **kwargs)
        self.my_extra_param = 'hello world'

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

@receiver(post_save, sender=MyModel)
def process_my_param(sender, instance, *args, **kwargs):
    my_extra_param = instance.my_extra_param

4
投票

如果预定义的信号不合适,则可以随时定义自己的信号。

import django.dispatch

custom_post_save = django.dispatch.Signal(providing_args=[
    "sender", "instance", "created", "raw", "using", "update_fields", "fragment_name"
])

然后在模型中,您只需要重写save()方法:

from django.db import router

class YourModel(Model):

    # Your fields and methods

    def save(self, force_insert=False, force_update=False, using=None,
         update_fields=None):
         custom_signal_kwargs = {
             "sender": self.__class__,
             "instance": self,
             "created": self.pk is None,
             "raw": False, # As docs say, it's True only for fixture loading
             "using": using or router.db_for_write(self.__class__, instance=self),
             "update_fields": update_fields,
             "fragment_name": "categories_index" # The thing you want
         }
         super(YourModel, self).save(force_insert=False, force_update=False, using=None,
             update_fields=None)
         custom_post_save.send(**custom_signal_kwargs) # Send custom signal

现在您只需要将此自定义信号连接到notify(...)接收器,它将以kwargs形式获得fragment_name


1
投票

[Django中负责Signals的代码在https://github.com/django/django/blob/master/django/dispatch/dispatcher.py中定义。看看它如何检查接收器?我怀疑您的问题就在那儿。也许您想要的是一个包装函数,该函数接受信号需要具有的参数,但还可以设置fragment_name的值。

def fragment_receiver(sender, **kwargs)
    return notify(sender, fragment_name="categories_index", **kwargs)

0
投票

[我尝试了尤金·索达托夫的答案,但是这使我意识到它可能更简单:

您可能会有类似的内容:

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

然后像Eugene的回答一样让接收者,它会一样工作。

@receiver(post_save, sender=MyModel)
def process_my_param(sender, instance, *args, **kwargs):
    my_extra_param = instance.my_extra_param

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

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

最新问题
© www.soinside.com 2019 - 2025. All rights reserved.