如何在Rails中使用dependent: :destroy时跳过指定的destroy回调
实现方案
核心思路是给子模型增加上下文标记,区分销毁触发来源,再给需要跳过的回调增加执行条件即可,完全无需使用delete/delete_all方法。
步骤1:给子模型增加销毁来源标记
无需新增数据库字段,用内存属性即可标记当前销毁是否由父模型关联触发,同时给需要跳过的回调增加条件判断:
class ChildA < ApplicationRecord belongs_to :parent # 内存标记位,标识销毁触发来源 attr_accessor :destroyed_by_parent before_destroy :execute_on_all_destroy # 仅非父模型触发销毁时,才执行该回调 before_destroy :execute_except_dependent_destroy, if: -> { !destroyed_by_parent } def execute_on_all_destroy # 所有销毁场景都要执行的逻辑 end def execute_except_dependent_destroy # 仅独立销毁时才执行的逻辑,父级关联销毁时自动跳过 end end
注意修正你示例代码中的拼写错误:
belong_to应为belongs_to,beofre_destroy应为before_destroy。
步骤2:自定义父模型关联销毁逻辑
替换原有的dependent: :destroy配置,自定义销毁逻辑,销毁子对象前先给标记位赋值:
class Parent < ApplicationRecord has_many :child_as # 父模型销毁前先处理子对象 before_destroy :destroy_related_children private def destroy_related_children child_as.each do |child| child.destroyed_by_parent = true child.destroy end # 其余子模型的销毁逻辑可以直接追加在这里 end end
可选优化:抽取公共模块适配10个子模型场景
如果有多个子模型需要复用该逻辑,可以把核心能力抽成Concern模块,避免重复代码:
- 子模型通用模块(
app/models/concerns/skip_parent_destroy_callbacks.rb)
module SkipParentDestroyCallbacks extend ActiveSupport::Concern included do attr_accessor :destroyed_by_parent end def not_destroyed_by_parent? !destroyed_by_parent end end
子模型直接引入即可:
class ChildA < ApplicationRecord include SkipParentDestroyCallbacks belongs_to :parent before_destroy :execute_on_all_destroy before_destroy :execute_except_dependent_destroy, if: :not_destroyed_by_parent? end
- 父模型通用模块(
app/models/concerns/custom_dependent_destroy.rb)
module CustomDependentDestroy extend ActiveSupport::Concern class_methods do # 自定义关联宏,批量定义支持跳过回调的关联 def has_many_with_skip_callbacks(name, **opts) has_many name, **opts before_destroy do send(name).each do |record| record.destroyed_by_parent = true record.destroy end end end end end
父模型引入后可以批量定义10个子模型关联:
class Parent < ApplicationRecord include CustomDependentDestroy has_many_with_skip_callbacks :child_as has_many_with_skip_callbacks :child_bs has_many_with_skip_callbacks :child_cs # 剩余7个子模型依次配置即可,无需额外写销毁逻辑 end
内容的提问来源于stack exchange,提问作者Praveenkumar
相关产品推荐
相关产品推荐

