Rails:如果因为父被销毁而被销毁时如何禁用before_destroy回调(:dependent =>:destroy)

Jos*_*rco 19 ruby-on-rails ruby-on-rails-3.1

我有两个班:父母和孩子

儿童:

belongs_to :parent
Run Code Online (Sandbox Code Playgroud)

has_many :children, :dependent => :destroy
Run Code Online (Sandbox Code Playgroud)

问题是我想检查总是至少有一个孩子存在,所以我在Child中有一个before_destroy方法,如果它是属于其父项的唯一子项,则中止destroy.

并且,如果我想要销毁父节点,它将在每个子节点上调用before_destroy回调,但是当有一个子节点时,它将中止销毁,因此父节点永远不会被销毁.

如果孩子因为父母没有被销毁,我怎么能告诉孩子调用before_destroy回调呢?

谢谢!

Ale*_*x D 11

has_many :childs, :dependent => :delete_all
Run Code Online (Sandbox Code Playgroud)

这将删除所有孩子而不运行任何钩子.

您可以在以下网址找到相关文档:http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html#method-i-has_many

  • 谢谢!这有效,但我想知道是否有任何方法可以告诉所有孩子他们正在从a:dependent =>:destroy callback中被销毁.因为我想在我的Child类上调用其他的before_destroy回调函数 (2认同)

dbo*_*rtz 5

如果您在 before_destroy 方法上将 prepend 设置为 true,则 carp 的上述答案将起作用。尝试这个:

孩子:

belongs_to :parent
before_destroy :prevent_destroy
attr_accessor :destroyed_by_parent

...

private

def prevent_destroy
  if !destroyed_by_parent
    self.errors[:base] << "You may not delete this child."
    return false
  end
end
Run Code Online (Sandbox Code Playgroud)

家长:

has_many :children, :dependent => :destroy
before_destroy :set_destroyed_by_parent, prepend: true

...

private

def set_destroyed_by_parent
  children.each{ |child| child.destroyed_by_parent = true }
end
Run Code Online (Sandbox Code Playgroud)

我们必须这样做,因为我们使用的是 Paranoia,并且dependent: delete_all会硬删除而不是软删除它们。我的直觉告诉我有一种更好的方法可以做到这一点,但它并不明显,而且这可以完成工作。


Ale*_*pov 5

在Rails 4中,您可以执行以下操作:

class Parent < AR::Base
  has_many :children, dependent: :destroy
end

class Child < AR::Base
  belongs_to :parent

  before_destroy :check_destroy_allowed, unless: :destroyed_by_association

  private

  def check_destroy_allowed
    # some condition that returns true or falls
  end
end
Run Code Online (Sandbox Code Playgroud)

这样,当destroy直接在子级上check_destroy_allowed调用时,将运行该回调,但在destroy父级上调用时,则不会。