Rails / Ruby:使用dependent :: destroy的自引用关联导致重复的回调

时间:2014-08-10 18:33:31

标签: ruby-on-rails ruby callback associations

很难在标题中正确地说出我的问题,但我会尝试更详细地解释一下。

我需要在我的一个模型中使用自引用关联,如下所示:

class StandingEvent < Event
  has_many :children, class_name: "StandingEvent", foreign_key: "parent_id", dependent: :destroy
  belongs_to :parent, class_name: "StandingEvent"

  after_destroy :do_stuff

  ...
end

问题是,当我在一系列StandingEvent.destroy记录上调用StandingEvent方法时 - 例如来自其他相关模型的记录 - 任何StandingEvent记录已分配的parent_id总是运行after_destroy回调方法两次

当我想销毁集合中的所有StandingEvent条记录时,这是一个问题,例如代码中其他地方调用的此方法:

def destroy_standing_events
  self.standing_events.each do |standing_event|
    standing_event.destroy
  end
end

我当然已经收集到,这是dependent: :destroy has_many :children关联使用destroy的预期行为,因为如果存在,则显然会先为孩子调用destroy 然后为原始父母调用destroy_standing_events。但是,在上面的destroy示例中,这是一个问题,因为即使在通过dependent: :destroy子句在子记录上调用destroy方法之后,它也会生成after_destroy :do_stuff }再次呼叫,第二次触发我的after_destroy回叫。

尝试解决方案

到目前为止,我已尝试过以下方法(未成功):

  • before_destroy更改为standing_event.destroyed?,以防万一回调订单质朴。
  • 尝试在外部destroy_standing_events方法和内部:do_stuff回调方法中检查StandingEvent状态
  • 尝试使用附加到:do_stuff模型的自定义标记来指示destroy_standing_events的操作是否已经应用于&#34; hack&#34;一种防止重复的方法。

不幸的是,到目前为止唯一的&#34;解决方案&#34;我发现基本上是在外部destroy方法中运行辅助数据库查找,以确保在完成def destroy_standing_events self.standing_events.each do |standing_event| # Verify that each event exists standing_event = StandingEvent.find_by(id: standing_event.id) standing_event.destroy unless standing_event.nil? end end 调用之前记录尚未被销毁,如下所示:< / p>

StandingEvents

所以,虽然上述解决方案在技术上有效,但我无法帮助但感觉那里有更好的解决方案,特别是那些不需要这么多额外数据库查询的解决方案。

非常感谢任何指导或帮助!

感谢@ pdobb的建议,我通过将返回的StandingEvent < Event has_many :children, class_name: "StandingEvent", foreign_key: "parent_id", dependent: :destroy belongs_to :parent, class_name: "StandingEvent" def self.dominant where.any_of(no_parent.merge(no_children), has_children) end def self.has_children includes(:children).where.not(children_events: { id: nil }) end def self.no_children includes(:children).where(children_events: { id: nil }) end def self.no_parent where(parent: nil) end 的集合仅限于仅限于那些记录而仅使用一个数据库查询来找出我的特定问题的解决方案任何父/子关联记录, OR 父母的记录。

StandingEvent

上面是no_parent模型的相关代码,我在其中添加了四个类方法来查询特定的集合。然后我抓住no_childrenhas_many :children, dependent: :destroy集合的合并,它代表所有没有可以自由销毁的关联的记录。然后,由于StandingEvent中的children子句,我还需要使用has_children方法包含那些dominant的记录。

为了简化通话,以上所有内容都合并到dominant方法中,我只使用ActiverecordAnyOf gem创建一个简单的&#34; OR&#34;数据库查询(虽然没有 AnyOf gem的相同结果可以通过自定义SQL或AREL完成。)

根据@pdobb的建议,最终结果是我可以在外线通话中将destroyed记录称为def destroy_standing_events self.standing_events.dominant.destroy_all end

{{1}}

2 个答案:

答案 0 :(得分:0)

如果只是首先尝试删除父母(不包括孩子)?

def destroy_standing_events
  standing_events.is_parent.destroy_all
end

其中is_child是在 StandingEvent 上定义的范围:

class StandingEvent < ActiveRecord::Base
  scope :is_parent, -> { where.not(parent_id: nil) }
end

然后你的循环不包括开始的孩子,dependent: :destroy选项会照顾破坏相关的孩子。注意:范围语法假定为Rails 4.x。

答案 1 :(得分:0)

假设它是一个无循环图,您可以执行以下操作 摆脱依赖的破坏

def self.dest_rec node
  node.children.each{ |c| dest_rec c }
  node.destroy
end

如果图表不是自由循环适应

 def self.dest_rec node, already_destroyed={}
   node.children.each do | c |
      next if already_destroyed[c.id]
      already_destroyed[c.id] = true # What a liar I am ;)
      dest_rec c, already_detroyed
   end
   node.destroy
 end

这是一个n db查找树高(或最长的非循环路径)的一般情况 of。

我们可以做得更好吗?