我正在研究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
)
notify
函数有一个关键字参数fragment_name
我希望在我的信号中默认传递。
有什么建议吗?
答案 0 :(得分:20)
您可以在模型的自定义保存方法中定义其他参数,如下所示:
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
答案 1 :(得分:12)
您对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
)
包含弱=假,这部分不会被垃圾收集。
我的原始答案如下,并采取了一种不使用偏见的方法。
您可以在将其与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)
答案 2 :(得分:3)
如果预定义信号不合适,您可以随时定义自己的信号。
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
。
答案 3 :(得分:1)
负责信号的Django代码在这里定义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)
答案 4 :(得分:0)
我尝试了尤金·索达托夫(Eugene Soldatov)的答案,但是这使我意识到它可能更简单:
您可能会遇到类似的事情:
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中的工作方式。我没有尝试过以前的版本。