web-dev-qa-db-fra.com

Passer les arguments Signaux Django - post_save/pre_save

Je travaille sur une application de notification dans Django 1.6 et je souhaite transmettre des arguments supplémentaires aux signaux Django tels que post_save. J'ai essayé d'utiliser partiel de functools mais pas de chance.

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

La fonction notify a un argument de mot clé fragment_name que je veux transmettre par défaut dans mes signaux.

Aucune suggestion?

19
Mo J. Mughrabi

Votre tentative avec partial ne fonctionne pas car, par défaut, ces récepteurs sont connectés à l'aide d'une référence faible.

Selon les Django docs :

Django stocke les gestionnaires de signaux sous forme de références faibles par défaut. Par conséquent, si votre gestionnaire est une fonction locale, il est possible que celle-ci soit collectée. Pour éviter cela, passez faible = False lorsque vous appelez le signal 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
    )

Inclure faible = Faux et ce partiel ne sera pas récupéré.

Ma réponse initiale est ci-dessous et a adopté une approche qui n'utilisait pas partielle.

Vous pouvez décorer votre fonction de sauvegarde des publications avant de la connecter au récepteur 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...

Le extra inner dans extra_args est pour décorateurs qui prennent des paramètres .

Si vous souhaitez effectuer cette opération par programme, cela fonctionne de la même manière, mais notez que vous devez inclure weak=False pour que la fonction encapsulée ne soit pas nettoyée.

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

Ou sans envelopper, mais en appelant post_save.connect comme votre tentative initiale avec

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

Vous pouvez définir des arguments supplémentaires dans la méthode de sauvegarde personnalisée du modèle, comme suit:

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

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

Et accédez à cet argument supplémentaire via une instance du récepteur de signal post_save:

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

Si les signaux prédéfinis ne conviennent pas, vous pouvez toujours définir les vôtres.

import Django.dispatch

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

Ensuite, dans votre modèle, il vous suffit de remplacer la méthode 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

Il ne vous reste plus qu'à connecter ce signal personnalisé à votre récepteur notify(...) et il obtiendra fragment_name en kwargs.

3
ElmoVanKielmo

Le code dans Django responsable des signaux est défini ici https://github.com/Django/django/blob/master/Django/dispatch/dispatcher.py . Voyez comment il inspecte le récepteur? Je soupçonne que vos problèmes sont là. Peut-être que ce que vous voulez, c'est une fonction wrapper qui respecte les arguments qu'un signal doit avoir, mais définit également la valeur de fragment_name.

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