Skip to content

Django Signal Disconnect Fix

DodaTech Updated 2026-06-24 2 min read

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_uid on signal receivers in reusable apps.
  • For project-level signals, use dispatch_uid or guard with has_listeners.
  • Disconnect signals in tests to isolate side effects.

Common Mistakes with signal disconnect

  1. Using return to exit a function early instead of wrapping a pure value in the monad
  2. Mixing let bindings with <- bindings in do notation, producing type errors
  3. 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

### What causes duplicate signal connections?

Django's dev server autoreload calls AppConfig.ready() on each reload without disconnecting old handlers. dispatch_uid prevents this.

Can I disconnect a signal from a third-party app?

Yes. Import the receiver function and call signal.disconnect(receiver, sender=Model).

Does dispatch_uid work with the @receiver decorator?

Yes. Pass dispatch_uid as an argument to @receiver.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro