diff --git a/factory/django.py b/factory/django.py index 23b93425..5d42e384 100644 --- a/factory/django.py +++ b/factory/django.py @@ -306,8 +306,13 @@ def __exit__(self, exc_type, exc_value, traceback): logger.debug('mute_signals: Restoring signal handlers %r', receivers) - signal.receivers += receivers with signal.lock: + new_receivers = signal.receivers + signal.receivers = receivers + for lookup_key, receiver in new_receivers: + # add dynamic receivers same way django signal adds them + if all(r_key != lookup_key for r_key, _ in signal.receivers): + signal.receivers.append((lookup_key, receiver)) # Django uses some caching for its signals. # Since we're bypassing signal.connect and signal.disconnect, # we have to keep messing with django's internals. diff --git a/tests/test_django.py b/tests/test_django.py index 2401dae6..e615a84e 100644 --- a/tests/test_django.py +++ b/tests/test_django.py @@ -927,10 +927,17 @@ def test_context_manager(self): self.assertSignalsReactivated() def test_receiver_created_during_model_instantiation_is_not_lost(self): + original_receiver_keys = [r_key for r_key, _ in signals.post_save.receivers] + with factory.django.mute_signals(signals.post_save): instance = WithSignalsFactory(post_save_signal_receiver=self.handlers.created_during_instantiation) self.assertTrue(self.handlers.created_during_instantiation.called) + restored_receiver_keys = [r_key for r_key, _ in signals.post_save.receivers] + self.assertTrue( + all(orig == restored for orig, restored in zip(original_receiver_keys, restored_receiver_keys)) + ) + self.handlers.created_during_instantiation.reset_mock() instance.save()