Pasando argumentos señales django-post_save/pre_save
signals argument-passing (4)
Estoy trabajando en una aplicación de notificación en Django 1.6 y quiero pasar argumentos adicionales a señales de Django como post_save
. Intenté usar parcial de functools pero no hubo suerte.
from functools import partial
post_save.connect(
receiver=partial(notify,
fragment_name="categories_index"),
sender=nt.get_model(),
dispatch_uid=nt.sender
)
notify
función de notify
tiene un argumento nombre de palabra clave, fragment_name
que deseo pasar como predeterminado en mis señales.
¿Alguna sugerencia?
El código en Django responsable de las señales se define aquí https://github.com/django/django/blob/master/django/dispatch/dispatcher.py . ¿Ves cómo inspecciona el receptor? Sospecho que tus problemas están ahí. Tal vez lo que desea es una función de envoltura que respete los argumentos que debe tener una señal, pero que también establezca el valor de nombre_de_grupo.
def fragment_receiver(sender, **kwargs)
return notify(sender, fragment_name="categories_index", **kwargs)
Puede definir argumentos adicionales en el método de guardado personalizado del modelo como este:
class MyModel(models.Model):
....
def save(self, *args, **kwargs):
super(MyModel, self).save(*args, **kwargs)
self.my_extra_param = ''hello world''
Y acceda a este argumento adicional a través de la instancia en el receptor de señal post_save:
@receiver(post_save, sender=MyModel)
def process_my_param(sender, instance, *args, **kwargs):
my_extra_param = instance.my_extra_param
Si las señales predefinidas no son adecuadas, siempre puede definir las suyas propias.
import django.dispatch
custom_post_save = django.dispatch.Signal(providing_args=[
"sender", "instance", "created", "raw", "using", "update_fields", "fragment_name"
])
Luego, en su Modelo, solo tiene que anular el método de 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
Ahora solo tiene que conectar esta señal personalizada a su receptor de notify(...)
y obtendrá fragment_name
en kwargs.
Su intento con parcial no está funcionando porque, de forma predeterminada, estos receptores están conectados mediante una referencia débil.
Según los documentos de Django :
Django almacena los manejadores de señales como referencias débiles de manera predeterminada, por lo que si su manejador es una función local, puede ser recolectada como basura. Para evitar esto, pase débil = Falso cuando llama a la señal 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
)
Incluya débil = Falso y este parcial no será recolectado como basura.
Mi respuesta original está abajo y tomé un enfoque que no estaba usando parcial.
Puede decorar su función de guardado posterior antes de conectarlo con el receptor 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...
El interior adicional en extra_args es para decoradores que toman parámetros .
Si desea hacer esto mediante programación, esto funciona de la misma manera, pero tenga en cuenta que debe incluir weak=False
para que la función envuelta no sea recogida de basura.
receiver(post_save, sender=aSenderClass, weak=False)(extra_args(fragment_name="meep")(my_post_save))
O sin envolver, pero llamando a post_save.connect como su intento original con parcial
post_save.connect(extra_args(fragment_name="meepConnect")(my_post_save), sender=Author, weak=False)