Subjects

All subjects Django Java Python React Spring Boot JavaScript PHP
Sign Up Free
Question 10 of 10 · Django Signals & Decorators
Interview question

What are advanced signal patterns like signal cascading and chained signals? Advanced signal patterns क्या हैं - signal cascading और chained signals?

Answer

Advanced signal patterns involve chaining signals (one signal triggers another), cascading effects through related models, and coordinating multiple signal handlers for complex workflows.

// Signal Cascading - One signal triggers another
from django.db.models.signals import post_save, post_delete

@receiver(post_save, sender=Book)
def update_author_stats(sender, instance, created, **kwargs):
    # When book is created/updated, update author stats
    author = instance.author
    author.total_books = author.books.count()
    author.avg_rating = author.books.aggregate(Avg("rating"))["rating__avg"]
    author.save()  # This triggers post_save for Author

@receiver(post_save, sender=Author)
def update_publisher_stats(sender, instance, **kwargs):
    # When author is saved (by signal above), update publisher stats
    publisher = instance.publisher
    publisher.total_authors = publisher.authors.count()
    publisher.save()  # Cascading continues

// Signal Coordination - Multiple handlers for same event
@receiver(post_save, sender=Book)
def handler1_cache_invalidation(sender, instance, **kwargs):
    cache.delete(f"books_list")

@receiver(post_save, sender=Book)
def handler2_search_index_update(sender, instance, **kwargs):
    update_search_index(instance)

@receiver(post_save, sender=Book)
def handler3_send_notification(sender, instance, **kwargs):
    notify_followers.delay(instance.id)

// ❌ PROBLEM - Circular Signal References
@receiver(post_save, sender=Book)
def update_category(sender, instance, **kwargs):
    category = instance.category
    category.book_count += 1
    category.save()  # Triggers post_save for Category

@receiver(post_save, sender=Category)
def update_books(sender, instance, **kwargs):
    # This could trigger book signal again!
    for book in instance.books.all():
        book.category_updated = True
        book.save()  # Back to book signal - circular!

// ✅ SOLUTION - Flag to prevent recursion
@receiver(post_save, sender=Book)
def update_category_safe(sender, instance, **kwargs):
    # Check if we're already in signal chain
    if getattr(instance, "_updating_category", False):
        return
    
    try:
        instance._updating_category = True
        category = instance.category
        category.book_count = category.books.count()
        category.save()
    finally:
        instance._updating_category = False

// Signal Chain Tracking - Debug cascading signals
class SignalLog:
    chain = []
    
    @classmethod
    def log_signal(cls, signal_name, model_name, action):
        cls.chain.append({
            "signal": signal_name,
            "model": model_name,
            "action": action,
            "time": timezone.now()
        })

@receiver(post_save, sender=Book)
def track_book_signal(sender, instance, created, **kwargs):
    SignalLog.log_signal("post_save", "Book", "created" if created else "updated")
    # Process...

@receiver(post_save, sender=Author)
def track_author_signal(sender, instance, **kwargs):
    SignalLog.log_signal("post_save", "Author", "updated")
    # Process...

// Async Signal Chain
from celery import chain, group

@receiver(post_save, sender=Book)
def process_book_async_chain(sender, instance, created, **kwargs):
    if created:
        # Chain: Task1 -> Task2 -> Task3
        workflow = chain(
            validate_book.s(instance.id),
            generate_thumbnail.s(),
            update_search_index.s()
        )
        workflow.apply_async()

// Signal Priority/Ordering
# Signals don't have built-in ordering, so use handler naming
# or explicit dispatcher management

def trigger_high_priority_signal():
    # Execute handlers in specific order
    handlers = [
        (handle_cache, 1),      # Priority 1 (highest)
        (handle_db_update, 2),  # Priority 2
        (handle_notification, 3) # Priority 3 (lowest)
    ]
    
    for handler, _ in sorted(handlers, key=lambda x: x[1]):
        handler()

// Conditional Signal Chaining
@receiver(post_save, sender=Order)
def process_order(sender, instance, created, **kwargs):
    if created:
        # Only chain if order is valid
        if instance.is_valid():
            order_chain = chain(
                process_payment.s(instance.id),
                send_confirmation.s(),
                update_inventory.s()
            )
            order_chain.apply_async()

// Batch Signal Processing
from django.db.models.signals import post_save
import threading

class SignalBatcher:
    queue = []
    lock = threading.Lock()
    
    @classmethod
    def batch_update(cls, model_instance):
        with cls.lock:
            cls.queue.append(model_instance)
            
            if len(cls.queue) >= 100:
                cls.flush()
    
    @classmethod
    def flush(cls):
        # Process 100 items at once instead of individually
        instances = cls.queue[:]
        cls.queue.clear()
        bulk_process(instances)

@receiver(post_save, sender=Book)
def batch_book_updates(sender, instance, **kwargs):
    SignalBatcher.batch_update(instance)

Advanced patterns में cascading (एक signal दूसरे को trigger करता है), coordination (multiple handlers), और async chains शामिल हैं।

// Cascading signals
@receiver(post_save, sender=Book)
def update_author(sender, instance, **kwargs):
    author = instance.author
    author.total_books = author.books.count()
    author.save()  # Triggers author signal

@receiver(post_save, sender=Author)
def update_publisher(sender, instance, **kwargs):
    publisher = instance.publisher
    publisher.save()  # Cascading continues

// Prevent circular cascades
if getattr(instance, "_updating", False):
    return

instance._updating = True
try:
    # Process signal
    pass
finally:
    instance._updating = False

Was this answer clear?