CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-django-stubs

Comprehensive type stubs for Django framework enabling static type checking with mypy

Pending
Overview
Eval results
Files

signals.mddocs/

Django Signals

Django's signal system provides decoupled notifications for actions happening throughout the framework, enabling loose coupling between applications through event-driven programming patterns.

Core Imports

# Signal base classes
from django.dispatch import Signal, receiver

# Model signals
from django.db.models.signals import (
    pre_init, post_init, pre_save, post_save,
    pre_delete, post_delete, m2m_changed,
    pre_migrate, post_migrate, class_prepared
)

# Core framework signals  
from django.core.signals import (
    request_started, request_finished,
    got_request_exception, setting_changed
)

# Authentication signals
from django.contrib.auth.signals import (
    user_logged_in, user_logged_out, user_login_failed
)

# Database backend signals
from django.db.backends.signals import connection_created

# Test signals
from django.test.signals import setting_changed as test_setting_changed

Capabilities

Signal Base Classes

Core signal infrastructure for creating and managing custom signals.

class Signal:
    """
    Base signal class for creating custom signals.
    
    Attributes:
        receivers: List of connected receiver functions
        lock: Threading lock for receiver management
        use_caching: Whether to cache receiver lookups
        sender_receivers_cache: Cache of receivers for specific senders
    """
    receivers: list[Any]
    lock: threading.Lock
    use_caching: bool
    sender_receivers_cache: MutableMapping[Any, Any]
    
    def __init__(self, use_caching: bool = True) -> None: ...
    
    def connect(self, receiver: Callable, sender: object | None = None, weak: bool = True, dispatch_uid: Hashable | None = None) -> None:
        """
        Connect receiver function to this signal.
        
        Args:
            receiver: Function to call when signal is sent
            sender: Specific sender to listen for (None for all)
            weak: Whether to use weak references
            dispatch_uid: Unique identifier for this connection
        """
    
    def disconnect(self, receiver: Callable | None = None, sender: object | None = None, dispatch_uid: str | None = None) -> bool:
        """
        Disconnect receiver from this signal.
        
        Args:
            receiver: Function to disconnect
            sender: Specific sender to disconnect from
            dispatch_uid: Unique identifier of connection to remove
            
        Returns:
            Whether a receiver was disconnected
        """
    
    def has_listeners(self, sender: Any = None) -> bool:
        """Check if signal has any listeners for given sender."""
    
    def send(self, sender: Any, **named: Any) -> list[tuple[Callable, str | None]]:
        """
        Send signal to all connected receivers.
        
        Args:
            sender: Object sending the signal
            **named: Additional keyword arguments for receivers
            
        Returns:
            List of (receiver, response) tuples
        """
    
    def send_robust(self, sender: Any, **named: Any) -> list[tuple[Callable, Exception | Any]]:
        """
        Send signal to all receivers, catching exceptions.
        
        Args:
            sender: Object sending the signal
            **named: Additional keyword arguments for receivers
            
        Returns:
            List of (receiver, response_or_exception) tuples
        """

def receiver(signal: Signal | list[Signal] | tuple[Signal, ...], *, sender: object | None = None, weak: bool = True, dispatch_uid: Hashable | None = None) -> Callable:
    """
    Decorator for connecting functions to signals.
    
    Args:
        signal: Signal or signals to connect to
        sender: Specific sender to listen for
        weak: Whether to use weak references
        dispatch_uid: Unique identifier for connection
        
    Returns:
        Decorator function
    """

Model Signals

Signals sent during model lifecycle events.

class ModelSignal(Signal):
    """
    Signal subclass for model-related events with enhanced connect/disconnect methods.
    """
    def connect(self, receiver: Callable, sender: type[Model] | str | None = None, weak: bool = True, dispatch_uid: str | None = None, apps: Apps | None = None) -> None:
        """
        Connect receiver to model signal.
        
        Args:
            receiver: Function to call when signal is sent
            sender: Model class or app label to listen for
            weak: Whether to use weak references
            dispatch_uid: Unique identifier for connection
            apps: Apps registry for lazy signal connections
        """
    
    def disconnect(self, receiver: Callable | None = None, sender: type[Model] | str | None = None, dispatch_uid: str | None = None, apps: Apps | None = None) -> bool | None:
        """Disconnect receiver from model signal."""

# Model lifecycle signals
pre_init: ModelSignal       # Before Model.__init__()
post_init: ModelSignal      # After Model.__init__()
pre_save: ModelSignal       # Before Model.save()
post_save: ModelSignal      # After Model.save()
pre_delete: ModelSignal     # Before Model.delete()
post_delete: ModelSignal    # After Model.delete()
m2m_changed: ModelSignal    # When ManyToManyField changes
class_prepared: Signal      # When model class is prepared

# Migration signals
pre_migrate: Signal         # Before migration
post_migrate: Signal        # After migration

Model Signal Usage Examples:

from django.db.models.signals import post_save
from django.dispatch import receiver
from myapp.models import User

@receiver(post_save, sender=User)
def user_post_save(sender, instance, created, **kwargs):
    if created:
        # Handle new user creation
        send_welcome_email(instance)
    else:
        # Handle user update
        update_search_index(instance)

# Connect without decorator
def my_handler(sender, **kwargs):
    pass

post_save.connect(my_handler, sender=User)

Core Framework Signals

Signals for core Django framework events.

# Request/response cycle signals
request_started: Signal      # When Django starts processing request
request_finished: Signal     # When Django finishes processing request
got_request_exception: Signal # When request processing raises exception

# Configuration signals
setting_changed: Signal      # When Django setting is changed

Core Signal Usage Examples:

from django.core.signals import request_started
from django.dispatch import receiver

@receiver(request_started)
def my_request_started_handler(sender, environ, **kwargs):
    # Handle request start
    log_request_started(environ)

# Settings change handler
@receiver(setting_changed)
def setting_changed_handler(sender, setting, value, enter, **kwargs):
    if setting == 'DEBUG':
        configure_logging(value)

Authentication Signals

Signals for user authentication events.

# Authentication events (from django.contrib.auth.signals)
user_logged_in: Signal       # When user logs in
user_logged_out: Signal      # When user logs out  
user_login_failed: Signal    # When login attempt fails

Authentication Signal Usage Examples:

from django.contrib.auth.signals import user_logged_in
from django.dispatch import receiver

@receiver(user_logged_in)
def user_logged_in_handler(sender, request, user, **kwargs):
    # Track user login
    LoginHistory.objects.create(
        user=user,
        ip_address=request.META.get('REMOTE_ADDR'),
        timestamp=timezone.now()
    )

Database Backend Signals

Signals for database connection events.

# Database backend events
connection_created: Signal   # When database connection is created

Custom Signal Creation

Creating and using custom signals for application-specific events.

# Creating custom signals
from django.dispatch import Signal

# Custom signal with specific arguments
order_completed = Signal()

# Signal with documented arguments
payment_processed = Signal()  # providing_args=['amount', 'payment_method']

Custom Signal Usage Examples:

from django.dispatch import Signal, receiver

# Define custom signal
order_completed = Signal()

# Send signal
def complete_order(order):
    # Complete order logic
    order.status = 'completed'
    order.save()
    
    # Send signal
    order_completed.send(
        sender=order.__class__,
        instance=order,
        total=order.total,
        user=order.user
    )

# Handle custom signal
@receiver(order_completed)
def handle_order_completion(sender, instance, total, user, **kwargs):
    # Send confirmation email
    send_order_confirmation(user.email, instance)
    
    # Update inventory
    update_inventory_for_order(instance)
    
    # Analytics tracking
    track_order_completion(user, total)

Advanced Signal Patterns

Conditional Signal Handling

@receiver(post_save, sender=User)
def conditional_handler(sender, instance, created, **kwargs):
    # Only handle specific conditions
    if created and instance.is_active:
        send_activation_email(instance)

Signal Disconnection

# Temporary disconnection
from django.db.models.signals import post_save

def bulk_create_users(user_data_list):
    # Disconnect signal to avoid sending emails during bulk creation
    post_save.disconnect(send_welcome_email, sender=User)
    
    try:
        User.objects.bulk_create([
            User(**data) for data in user_data_list
        ])
    finally:
        # Reconnect signal
        post_save.connect(send_welcome_email, sender=User)

Robust Signal Handling

# Handle exceptions in signal receivers
from django.db.models.signals import post_save
import logging

logger = logging.getLogger(__name__)

@receiver(post_save, sender=User)
def robust_post_save_handler(sender, instance, **kwargs):
    try:
        # Potentially failing operation
        external_api_call(instance)
    except Exception as e:
        # Log error but don't break the main flow
        logger.error(f"Failed to sync user {instance.id}: {e}")

Many-to-Many Signal Handling

from django.db.models.signals import m2m_changed
from django.dispatch import receiver

@receiver(m2m_changed, sender=User.groups.through)
def groups_changed(sender, instance, action, pk_set, **kwargs):
    if action == 'post_add':
        # Handle groups added to user
        for group_id in pk_set:
            assign_group_permissions(instance, group_id)
    elif action == 'post_remove':
        # Handle groups removed from user  
        for group_id in pk_set:
            revoke_group_permissions(instance, group_id)

Signal Best Practices

  1. Keep signal handlers lightweight - Avoid heavy processing in signal handlers
  2. Use robust error handling - Signal exceptions can break the main application flow
  3. Be careful with database operations - Signal handlers run in the same transaction
  4. Document signal dependencies - Make signal relationships clear
  5. Test signal behavior - Include signal testing in your test suite
  6. Use dispatch_uid for testing - Prevents duplicate connections during tests

Testing Signals

from django.test.utils import override_settings
from django.db.models.signals import post_save

class SignalTestCase(TestCase):
    def test_signal_handler(self):
        # Track signal calls
        signal_calls = []
        
        def test_handler(sender, **kwargs):
            signal_calls.append(kwargs)
        
        post_save.connect(test_handler, sender=User)
        
        try:
            user = User.objects.create(username='test')
            self.assertEqual(len(signal_calls), 1)
            self.assertEqual(signal_calls[0]['instance'], user)
        finally:
            post_save.disconnect(test_handler, sender=User)

Install with Tessl CLI

npx tessl i tessl/pypi-django-stubs

docs

admin.md

auth.md

contrib.md

database-orm.md

forms.md

http.md

index.md

migrations.md

mypy-plugin.md

signals.md

templates.md

transactions.md

urls.md

views.md

tile.json