Passing arguments django signals - post_save/pre_save

为君一笑 提交于 2019-11-30 06:14:47
Daniel Rucci

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)
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!