Skip to content

Instantly share code, notes, and snippets.

@bruce-shi
Forked from RobertKolner/disable_signals.py
Created September 18, 2016 02:44
Show Gist options
  • Star 26 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save bruce-shi/fd0e3f5e2360c64bc9ce2efb254744f7 to your computer and use it in GitHub Desktop.
Save bruce-shi/fd0e3f5e2360c64bc9ce2efb254744f7 to your computer and use it in GitHub Desktop.
Temporarily disable all signals in django.
from collections import defaultdict
from django.db.models.signals import *
class DisableSignals(object):
def __init__(self, disabled_signals=None):
self.stashed_signals = defaultdict(list)
self.disabled_signals = disabled_signals or [
pre_init, post_init,
pre_save, post_save,
pre_delete, post_delete,
pre_migrate, post_migrate,
]
def __enter__(self):
for signal in self.disabled_signals:
self.disconnect(signal)
def __exit__(self, exc_type, exc_val, exc_tb):
for signal in self.stashed_signals.keys():
self.reconnect(signal)
def disconnect(self, signal):
self.stashed_signals[signal] = signal.receivers
signal.receivers = []
def reconnect(self, signal):
signal.receivers = self.stashed_signals.get(signal, [])
del self.stashed_signals[signal]
# Example usage:
# with DisableSignals():
# user.save() # will not call any signals
@giorgifti
Copy link

For Python 3:

from collections import defaultdict
from django.db.models.signals import *


class DisableSignals(object):
    def __init__(self, disabled_signals=None):
        self.stashed_signals = defaultdict(list)
        self.disabled_signals = disabled_signals or [
            pre_init, post_init,
            pre_save, post_save,
            pre_delete, post_delete,
            pre_migrate, post_migrate,
        ]

    def __enter__(self):
        for signal in self.disabled_signals:
            self.disconnect(signal)

    def __exit__(self, exc_type, exc_val, exc_tb):
        for signal in list(self.stashed_signals):
            self.reconnect(signal)

    def disconnect(self, signal):
        self.stashed_signals[signal] = signal.receivers
        signal.receivers = []

    def reconnect(self, signal):
        signal.receivers = self.stashed_signals.get(signal, [])
        del self.stashed_signals[signal]


# Example usage:
# with DisableSignals():
#   user.save()  # will not call any signals

@abdhx
Copy link

abdhx commented Oct 7, 2019

Note: if you get RuntimeError: dictionary changed size during iteration, it's because you are using python 3. Use the python 3 version above.

@johngian
Copy link

johngian commented Nov 7, 2019

I tried this while migrating a DB. I think you might want to add m2m_changed as well.

@gdvalderrama
Copy link

It's not hard to avoid the star import from django.db.models.signals import *, I'd do the imports explicitly

@ZaakirHussain5
Copy link

will it work with serializer.save() ??

@isaacphi
Copy link

isaacphi commented Dec 7, 2022

I recommend adding signal.sender_receivers_cache.clear() to the reconnect method to avoid hours of debugging 😢

@joseluratm
Copy link

I recommend adding signal.sender_receivers_cache.clear() to the reconnect method to avoid hours of debugging 😢

I love you man

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment