usar trigger español como before django django-signals

trigger - Desconecta señales para modelos y reconecta en django.



events django (5)

Necesitaba evitar que ciertas señales se dispararan durante las pruebas de unidad, así que hice un decorador basado en la respuesta de qris:

from django.db.models import signals def prevent_signal(signal_name, signal_fn, sender): def wrap(fn): def wrapped_fn(*args, **kwargs): signal = getattr(signals, signal_name) signal.disconnect(signal_fn, sender) fn(*args, **kwargs) signal.connect(signal_fn, sender) return wrapped_fn return wrap

Usarlo es simple:

@prevent_signal(''post_save'', my_signal, SenderClass) def test_something_without_signal(self): # the signal will not fire inside this test

Necesito hacer un ahorro con un modelo pero necesito desconectar algunos receptores de las señales antes de guardarlo.

Quiero decir,

Tengo un modelo:

class MyModel(models.Model): ... def pre_save_model(sender, instance, **kwargs): ... pre_save.connect(pre_save_model, sender=MyModel)

y en otro lugar del código necesito algo como:

a = MyModel() ... disconnect_signals_for_model(a) a.save() ... reconnect_signals_for_model(a)

Como necesito en este caso, guarde el modelo sin ejecutar la función pre_save_model.


No he probado el siguiente código, pero debería funcionar:

from django.db.models.signals import pre_save def save_without_the_signals(instance, *args, **kwargs): receivers = pre_save.receivers pre_save.receivers = [] new_instance = instance.save(*args, **kwargs) pre_save.receivers = receivers return new_instance

instance.__class__ señales de todos los remitentes, aunque no solo de la instance.__class__ .

Esta versión deshabilita solo las señales del modelo dado:

from django.db.models.signals import pre_save from django.dispatch.dispatcher import _make_id def save_without_the_signals(instance, *args, **kwargs): receivers = [] sender_id = _make_id(instance.__class__) for index in xrange(len(self.receivers)): if pre_save.receivers[index][0][1] == sender_id: receivers.append(pre_save.receivers.pop(index)) new_instance = instance.save(*args, **kwargs) pre_save.receivers.extend(receivers) return new_instance


Para una solución limpia y reutilizable, puede usar un administrador de contexto:

class temp_disconnect_signal(): """ Temporarily disconnect a model from a signal """ def __init__(self, signal, receiver, sender, dispatch_uid=None): self.signal = signal self.receiver = receiver self.sender = sender self.dispatch_uid = dispatch_uid def __enter__(self): self.signal.disconnect( receiver=self.receiver, sender=self.sender, dispatch_uid=self.dispatch_uid, weak=False ) def __exit__(self, type, value, traceback): self.signal.connect( receiver=self.receiver, sender=self.sender, dispatch_uid=self.dispatch_uid, weak=False )

Ahora, puedes hacer algo como lo siguiente:

from django.db.models import signals from your_app.signals import some_receiver_func from your_app.models import SomeModel ... kwargs = { ''signal'': signals.post_save, ''receiver'': some_receiver_func, ''sender'': SomeModel, ''dispatch_uid'': "optional_uid" } with temp_disconnect_signal(**kwargs): SomeModel.objects.create( name=''Woohoo'', slug=''look_mom_no_signals'', )

Nota: Si su manejador de señales usa un dispatch_uid , DEBE usar el dispatch_uid arg.


Puede conectar y desconectar señales como lo hace Haystack en RealTimeSearchIndex, lo que parece más estándar:

from django.db.models import signals signals.pre_save.disconnect(pre_save_model, sender=MyModel) a.save() signals.pre_save.connect(pre_save_model, sender=MyModel)


Si solo desea desconectar y volver a conectar una señal personalizada, puede usar este código:

def disconnect_signal(signal, receiver, sender): disconnect = getattr(signal, ''disconnect'') disconnect(receiver, sender) def reconnect_signal(signal, receiver, sender): connect = getattr(signal, ''connect'') connect(receiver, sender=sender)

De esta manera podrás hacer esto:

disconnect_signal(pre_save, pre_save_model, MyModel) a.save() reconnect_signal(pre_save, pre_save_model, MyModel)