I use Django signals a lot in my professional work, mostly to create specialized tables that track events in the ecosystem of social networking sites that I build. For example, if I make a post on a social networking site, that causes an event that __creates a signal. That signal will be heard by, for example: (1) a reward mechanism, which might give me a badge/acheivement/sticker/shiny rock/whatever to acknowledge my place in the social network heirarchy, (2) a news mechanism, to look up who my friends are and tell them what I'm doing, (3) a logging mechanism, which will be of interest to my investors, (4) a social media mechanism, which will analyze my relationships with other social networking sites and ping them, among (5, 6, 7) whatever else you can think of.
These are all unique, filtered views of an action I just took that might serve me as agents of attention, reputation, and illumination.
As I've been working in this space, I've learned three very important rules for Django:
(1) Any Django application (not project, application) that builds its tables via signals and business logic rulesets must only and ever build its tables via signals and rulesets. It must not have its own views for doing so. It's CUD is signals. Only the R in CRUD may have views for the signal-built application.
(2) When dumping data for your project, never dump data from the signal-based applications. When you want to reload this data (after the appropriate mangling/filtering/whatever), the objects in your ecosystem models will send out the appropriate signals to build those tables for you. (Signal senders in your views that alter data? Shame on you!)
(3) As a consequence of (2), your signal-built data tables must take their dates from their instances. Otherwise, the signal-built tables become disordered with respect to the events they're expected to monitor.
Of course there are exceptions to these rules, but this is a very solid way to think about doing signal-based development.