信号能解耦,也能把业务流程藏起来。这篇不下定论,而是把「复杂业务放进信号」会遇到的几种情形一一跑出来:隐藏的查询、失败的传播、递归、顺序,最后对比同样逻辑写成显式函数的样子。
没有 receiver:一次 create() 的查询数 1 挂上 3 个 receiver 后同一行 create() 的查询数 7 (调用处代码完全没变)
create() 抛出: 库存服务不可用 没有 atomic:订单已经写入了吗? True 外面包了 atomic:订单还在吗? False
post_save 用的是 send(),receiver 的异常会直接抛给 save() 的调用方。有没有外层 atomic() 决定了是「半截业务」还是「整体回滚」。
RecursionError: maximum recursion depth exceeded (递归上限 1000 ) 改用 queryset.update()(不发信号):库里 status = paid
要在保存后改自己的字段,用 queryset.update()(不发信号),或者用标志位跳过。
connect 顺序 ['a', 'b'] → 执行顺序 ['a', 'b'] connect 顺序 ['b', 'a'] → 执行顺序 ['b', 'a']
place_order() 查询数: 6 (同样的写入,全部写在一个函数里,调用处能看到)
信号更适合「发布事件、别的模块可以订阅」这类松耦合的场景(比如缓存失效、日志、索引更新),而不是有先后依赖、必须全部成功的核心业务。这是设计判断,不是本文实验的结论;实验只说明了它的代价:隐藏的查询、失败传播、递归风险和顺序耦合。