如何从任意一端销毁双向has_one关联的左-中-右模型?
关联模型的双向级联销毁实现
模型代码
class Left < ApplicationRecord has_one :middle, dependent: :destroy has_one :right, through: :middle end class Middle < ApplicationRecord belongs_to :left, dependent: :destroy belongs_to :right, dependent: :destroy end class Right < ApplicationRecord has_one :middle, dependent: :destroy has_one :left, through: :middle end
需求与问题
希望调用left.destroy时,同时销毁对应的middle和right模型;调用right.destroy时,同时销毁对应的middle和left模型。
当前配置下,right.destroy能实现预期效果,但left.destroy不会销毁对应的right模型;调换Middle模型中belongs_to声明的顺序后,right.destroy失效,left.destroy正常工作。需要实现从任意一端触发左-中-右关联的完整销毁。
解决方案
问题核心是对belongs_to的dependent: :destroy逻辑理解偏差:这个选项的作用是当关联的主对象(如Left)被销毁时,销毁当前Middle对象,而非让Middle销毁时触发主对象的销毁。同时has_one :through关联默认不触发级联销毁,仅靠关联配置无法实现双向完整销毁。
可以通过在两端模型添加销毁回调的方式解决:
- 修改
Left模型,添加销毁后回调销毁关联的Right:
class Left < ApplicationRecord has_one :middle, dependent: :destroy has_one :right, through: :middle after_destroy :destroy_associated_right private def destroy_associated_right right&.destroy end end
- 修改
Right模型,添加销毁后回调销毁关联的Left:
class Right < ApplicationRecord has_one :middle, dependent: :destroy has_one :left, through: :middle after_destroy :destroy_associated_left private def destroy_associated_left left&.destroy end end
- 调整
Middle模型配置,移除belongs_to上的dependent: :destroy(Left和Right的has_one :middle, dependent: :destroy已经会触发Middle销毁,避免重复逻辑):
class Middle < ApplicationRecord belongs_to :left belongs_to :right end
效果说明
- 调用
left.destroy时,先由has_one :middle, dependent: :destroy销毁middle,再通过回调销毁关联的right; - 调用
right.destroy时,先销毁middle,再通过回调销毁关联的left; - 不会出现循环销毁问题,Rails会自动处理已销毁对象的状态判断,避免重复触发。
内容的提问来源于stack exchange,提问作者Andy Stewart
相关产品推荐
相关产品推荐

