Challenge 2: Write a post_delete Signal Receiver — Possible Solution ==================================================================== # catalog/signals.py from django.db.models.signals import post_delete from django.dispatch import receiver from .models import Book @receiver(post_delete, sender=Book) def log_book_deletion(sender, instance, **kwargs): print(f"Book deleted: {instance.title} (id={instance.pk})") # catalog/apps.py from django.apps import AppConfig class CatalogConfig(AppConfig): default_auto_field = "django.db.models.BigAutoField" name = "catalog" def ready(self): import catalog.signals # noqa: F401 — imported for its side effect (connecting signals) # mysite/settings.py — make sure the app config is actually used # (usually already correct by default, but worth confirming): INSTALLED_APPS = [ # ... "catalog.apps.CatalogConfig", # or simply "catalog", which Django resolves # to this same AppConfig by default in modern versions ] WHY THIS WORKS -------------- - @receiver(post_delete, sender=Book) connects log_book_deletion specifically to Book's post_delete signal — sender=Book means this function only fires when a Book instance is deleted, not for every model's deletions project-wide. - post_delete receivers get sender and instance as arguments, but NOT a `created` flag (that's specific to post_save) — instance here is the object that WAS just deleted, still accessible in memory even though its database row is already gone by the time this receiver runs. - The ready() method on CatalogConfig is the required wiring step: Django calls ready() once, when the app registry is fully loaded at startup, and importing catalog.signals from inside it is what actually executes the @receiver(...) decorator and connects the receiver function to the signal. Without this import happening somewhere, signals.py's code never runs at all — the receiver function would simply never be connected, and Book deletions would proceed silently with no log line, with no error anywhere pointing at why.