很难在标题中恰当地说明我的问题,但我将试着更详细地简要解释一下。
我需要在我的模型中使用自引用关联,例如:

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方法时(例如从另一个关联模型调用),任何具有已分配的StandingEventparent_id记录都将始终运行after_destroy回调方法两次。
当我想销毁集合中的所有StandingEvent记录时,这是一个问题,例如代码中其他地方调用的此方法:
def destroy_standing_events
  self.standing_events.each do |standing_event|
    standing_event.destroy
  end
end

当然,我收集到,这是预期的行为,使用dependent: :destroyhas_many :children关联,因为如果一个孩子存在,显然它首先调用了destroy,然后调用原来的父文件destroy。但是,在上面的destroy_standing_events示例中,这是一个问题,因为即使通过destroy子句对子记录调用了dependent: :destroy方法,它也会使destroy调用第二次,从而再次触发我的after_destroy :do_stuff回调。
尝试的解决方案
到目前为止,我已经尝试了以下方法(但没有成功):
after_destroy更改为before_destroy以防回调顺序有问题
尝试在outsidestanding_event.destroyed?方法和innerdestroy_standing_events回调方法中检查:do_stuff状态
试图使用附加到StandingEvent模型的自定义标志来指示:do_stuff的操作是否已应用于“hack”以防止重复。
不幸的是,到目前为止,我找到的唯一“解决方案”实际上是在outsidedestroy_standing_events方法中运行辅助数据库查找,以确保在完成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

所以,虽然上述解决方案在技术上是可行的,但我还是忍不住觉得有更好的解决方案,尤其是不需要太多额外数据库查询的解决方案。
任何指导或帮助将不胜感激!
解决方案
由于@pdobb在下面的建议,我仅使用一个数据库查询就能够解决我的特定问题,方法是将返回的StandingEvents集合限制为那些没有任何父/子关联记录或是父记录的记录。
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

上面是StandingEvent模型的相关代码,我在其中添加了四个类方法来查询特定的集合然后,我获取no_parentno_children集合的合并,这两个集合表示可以自由销毁的没有关联的所有记录然后,由于has_many :children, dependent: :destroy中的StandingEvent子句,我还需要使用children方法包含那些具有has_children的记录。
为了简化调用,将以上所有内容合并到dominant方法中,我使用ActiverecordAnyOf gem只是为了简单地创建一个简单的“或”数据库查询(尽管没有任何gem的相同结果可以通过自定义sql或arel来实现)。
@pdobb建议的最终结果是,我可以在外部调用中将dominant记录调用为destroyed
def destroy_standing_events
  self.standing_events.dominant.destroy_all
end

最佳答案

先尝试删除父级(不包括子级)如何?

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。

关于ruby-on-rails - Rails/Ruby:使用dependent::destroy的自引用关联导致重复的回调,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/25231774/

10-09 21:02