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 = FalseWas this answer clear?