很难在标题中恰当地说明我的问题,但我将试着更详细地简要解释一下。
我需要在我的模型中使用自引用关联,例如:
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
以防回调顺序有问题尝试在outside
standing_event.destroyed?
方法和innerdestroy_standing_events
回调方法中检查:do_stuff
状态试图使用附加到
StandingEvent
模型的自定义标志来指示:do_stuff
的操作是否已应用于“hack”以防止重复。不幸的是,到目前为止,我找到的唯一“解决方案”实际上是在outside
destroy_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_parent
和no_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/