Django Signal Disconnect Fix
In this tutorial, you'll learn about Django Signal Disconnect Fix. We cover key concepts, practical examples, and best practices.
The Problem
Signal receivers fire multiple times for a single event. This happens when ready() is called multiple times (dev server autoreload) or when signals are imported in multiple places.
Quick Fix
Wrong — signal fires multiple times
# Each reload re-connects the signal
# The handler fires 2x, 3x, or more per save
@receiver(post_save, sender=User)
def create_profile(sender, instance, created, **kwargs):
Profile.objects.get_or_create(user=instance)
Output: get_or_create prevents crashes, but the handler still runs multiple times. For email signals, users get duplicate emails.
Correct — disconnect before connect
from django.dispatch import receiver
from django.db.models.signals import post_save
def create_profile(sender, instance, created, **kwargs):
if created:
Profile.objects.get_or_create(user=instance)
# Disconnect first, then connect
post_save.disconnect(create_profile, sender=User)
post_save.connect(create_profile, sender=User)
Using dispatch_uid
@receiver(post_save, sender=User, dispatch_uid='create_user_profile')
def create_profile(sender, instance, created, **kwargs):
if created:
Profile.objects.create(user=instance)
Output: dispatch_uid ensures the signal is only connected once, even if ready() is called multiple times.
Checking if connected
from django.db.models.signals import post_save
def connect_signal_once():
if not post_save.has_listeners(sender=User):
post_save.connect(create_profile, sender=User)
Debugging duplicate connections
import django.dispatch
# List all connected receivers for a signal
receivers = post_save._live_receivers(sender=User)
print(f"Connected receivers: {len(receivers)}")
for r in receivers:
print(f" {r}")
Temporary disconnect for testing
from django.test import TestCase
from django.db.models.signals import post_save
class ProfileTest(TestCase):
def setUp(self):
post_save.disconnect(create_profile, sender=User)
def tearDown(self):
post_save.connect(create_profile, sender=User)
def test_create_profile_manually(self):
user = User.objects.create(username='test')
profile = Profile.objects.create(user=user)
self.assertEqual(profile.user, user)
Prevention
- Always use
dispatch_uidon signal receivers in reusable apps. - For project-level signals, use
dispatch_uidor guard withhas_listeners. - Disconnect signals in tests to isolate side effects.
Common Mistakes with signal disconnect
- Using
returnto exit a function early instead of wrapping a pure value in the monad - Mixing let bindings with <- bindings in do notation, producing type errors
- Overlapping type class instances that cause GHC to reject the program with ambiguous dispatch errors
These mistakes appear frequently in real-world DJANGO code. DodaTech's contributors have identified these patterns through analysis of open-source projects and production systems.
Practice Exercise
Write a pure function that safely divides two integers using Maybe, then test it with edge cases like division by zero and negative numbers.
This exercise reinforces the concepts covered in this guide. Try implementing it before checking online solutions.
FAQ
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro