What this prompt does
This prompt builds a Django signals architecture for a [project_type] so apps can react to [trigger_events] without depending on each other directly. It creates post-save receivers for [post_save_models] that distinguish the created case from the updated case using the created flag, and on a new [primary_model] it fires [creation_actions] like sending confirmations and reserving inventory.
It also defines custom signals for [custom_signal_events] using django.dispatch.Signal with documented arguments, connected in [signal_app]/apps.py ready(), plus pre-save validation on [validation_models] enforcing [business_rules] with descriptive ValidationErrors, and m2m_changed handlers for [m2m_relationships] reacting to [m2m_actions]. The key discipline is that signal handlers stay lightweight: heavy work ([heavy_operations]) is dispatched to [task_queue] rather than running in the request cycle. It also includes the disconnect/reconnect pattern for [bulk_operations] so signals don't fire during imports, plus an isolation-based testing strategy using [test_pattern] that tests each receiver on its own. The result is an event seam where each app announces what happened and other apps subscribe without importing one another.
When to use it
- You want apps decoupled so placing an order doesn't import email and inventory code
- You need different behavior on object creation versus update in one receiver
- You're enforcing business invariants like totals matching line items at save time
- You need to react when many-to-many relationships change (categories, promotions)
- You're doing bulk imports and need to suppress signals to avoid side effects
- You want each receiver unit-tested in isolation with disconnect/reconnect
- You're untangling tightly coupled apps and want events to flow through a clear seam
Example output
The AI returns receiver functions wired to post_save, pre_save, and m2m_changed, custom Signal definitions with documented arguments, an apps.py ready() connecting them, examples of dispatching [heavy_operations] to [task_queue], a disconnect/reconnect context manager for [bulk_operations], and tests that verify signals fire with the expected arguments. Expect snippets grouped per numbered item, so you can adopt the validation, async, and bulk-suppression pieces independently.
Pro tips
- Split logic on the
createdflag in post-save receivers so updates don't re-run creation-only actions from[creation_actions] - Keep handlers thin — anything in
[heavy_operations]should be a[task_queue]dispatch, not synchronous work in the request - Use the disconnect/reconnect pattern for every path in
[bulk_operations]; a CSV import firing per-row signals can grind to a halt - Connect signals in
apps.pyready(), not at module import, to avoid double-registration - Raise ValidationError with the specific rule from
[business_rules]in the message so failures are debuggable - Document the arguments your custom
[custom_signal_events]send, since receivers depend on that contract staying stable - React to
[m2m_actions]inm2m_changedrather thanpost_save, because the related rows aren't attached yet when the parent saves - If receivers are hard to test, lean on
[test_pattern]to mock heavy dependencies and assert signal arguments