I am working on a notification app in Django 1.6 and I want to pass additional arguments to Django signals such as post_save
. I tried to use partial from functools but no luck.
from functools import partial
post_save.connect(
receiver=partial(notify,
fragment_name="categories_index"),
sender=nt.get_model(),
dispatch_uid=nt.sender
)
notify
function has a keyword argument fragment_name
which I want to pass as default in my signals.
Any suggestions?
Your attempt with partial isn't working because by default these receivers are connected using a weak reference.
According to the Django docs:
Django stores signal handlers as weak references by default, so if your handler is a local function, it may be garbage collected. To prevent this, pass weak=False when you call the signal’s connect().
from functools import partial
post_save.connect(
receiver=partial(notify,
fragment_name="categories_index"),
sender=nt.get_model(),
dispatch_uid=nt.sender,
weak=False
)
Include weak=False and this partial won't be garbage collected.
My original answer is below and took an approach that wasn't using partial.
You could decorate your post save function prior to connecting it with the post_save receiver.
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...
The extra inner in extra_args is for decorators that take parameters.
If you want to do this programmatically this works the same way but note that you need to include weak=False
to have the wrapped function not be garbage collected.
receiver(post_save, sender=aSenderClass, weak=False)(extra_args(fragment_name="meep")(my_post_save))
Or without wrapping, but calling post_save.connect like your original attempt with partial
post_save.connect(extra_args(fragment_name="meepConnect")(my_post_save), sender=Author, weak=False)
You can define additional arguments in custom save method of model like this:
class MyModel(models.Model):
....
def save(self, *args, **kwargs):
super(MyModel, self).save(*args, **kwargs)
self.my_extra_param = 'hello world'
And access this additional argument through instance in post_save signal receiver:
@receiver(post_save, sender=MyModel)
def process_my_param(sender, instance, *args, **kwargs):
my_extra_param = instance.my_extra_param
If the predefined signals are not suitable, you can always define your own.
import django.dispatch
custom_post_save = django.dispatch.Signal(providing_args=[
"sender", "instance", "created", "raw", "using", "update_fields", "fragment_name"
])
Then in your Model you just have to override save()
method:
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
Now you just have to connect this custom signal to your notify(...)
receiver and it will get fragment_name
in kwargs.
The code in Django responsible for Signals is defined here https://github.com/django/django/blob/master/django/dispatch/dispatcher.py. See how it inspects the receiver? I suspect your problems lie there. Maybe what you want is a wrapper function that honors the arguments a signal needs to have but also sets the value of fragment_name.
def fragment_receiver(sender, **kwargs)
return notify(sender, fragment_name="categories_index", **kwargs)
来源:https://stackoverflow.com/questions/22999630/passing-arguments-django-signals-post-save-pre-save