我有两个课程:父母和孩子
子:
belongs_to :parent
和
父
has_many :children, :dependent => :destroy
问题是我想检查总是至少有一个孩子存在,所以我在Child中有一个before_destroy方法,如果它是属于其父级的唯一子项,则中止该销毁。
并且,如果我想要销毁父节点,它将在每个子节点上调用before_destroy回调,但是当有一个子节点时,它将中止销毁,因此父节点永远不会被销毁。
如果孩子因为父母而没有被销毁,我怎么能告诉孩子调用before_destroy回调呢?
谢谢!
答案 0 :(得分:11)
has_many :childs, :dependent => :delete_all
这将删除所有子项,而不运行任何挂钩。
您可以在以下网址找到相关文档:http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html#method-i-has_many
答案 1 :(得分: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
这样,直接在某个孩子上调用destroy
会运行check_destroy_allowed
回调,但当您在父母上调用destroy
时,则不会。{/ p>
答案 2 :(得分:4)
子:
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
父:
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
我们必须这样做,因为我们正在使用偏执狂,而dependent: delete_all
会硬删除而不是软删除它们。我的直觉告诉我,这是一个更好的方法,但这并不明显,这就完成了工作。
答案 3 :(得分:1)
可能有一种方法可以用不那么笨拙的方式来实现这一点,但这是一个(未经测试的!)想法:向attr_accessor :destroyed_by_parent
添加Child
并编辑Child的before_destroy过滤器以允许在true
时进行销毁{1}}。
将一个before_destroy过滤器添加到Parent
,迭代其所有子项:
private
# custom before_destroy
def set_destroyed_by_parent
self.children.each {|child| child.destroyed_by_parent = true }
end
如果由:dependent => :destroy
触发的销毁在Parent对象的实例化子节点上执行,则它可以工作。如果它单独实例化子项,它将无法工作。
答案 4 :(得分:1)
接受的答案并不能解决原来的问题。何塞想要两件事:
1)确保父母至少有一个孩子
和
2)能够在删除父母时删除所有孩子
您不需要任何before_destroy
回调来阻止删除孩子。
我写了detailed blog post describing the solution,但我也会在这里介绍基础知识。
解决方案包括各种成分:在Parent模型中使用状态验证和嵌套属性,并确保删除子项的方法不会在子项上调用.destroy
,但是孩子是通过嵌套属性从父模型中删除。
在父模型中:
attr_accessible :children_attributes
has_many :children, dependent: :destroy
accepts_nested_attributes_for :children, allow_destroy: true
validates :children, presence: true
在儿童模型中:
belongs_to :parent
接下来,除最后一项外,允许删除子项的最简单方法是使用嵌套表单,如Railscasts #196所述。基本上,您将拥有一个包含父项和子项字段的表单。对位置以及子项的任何更新(包括删除子项)都将由父控制器中的update
操作处理。
通过嵌套表单删除子项的方法是传入一个名为_destroy
的键,其值为true。我们在Parent模型中设置的allow_destroy: true
选项允许这样做。 Active Record Nested Attributes的文档涵盖了这一点,但这里有一个简单的示例,说明如何从其父级中删除id
等于2
的子项:
parent.children_attributes = { id: '2', _destroy: '1' }
parent.save
请注意,如果您使用Railscasts#196中的嵌套表单,则无需在父控制器中自行执行此操作。 Rails会为你处理它。
通过父模型中的状态验证,Rails将自动阻止删除最后一个子项。
我认为在Jose发布他的问题时,状态验证并没有按照预期的方式运行。这个pull request直到2012年7月才得到修复,但差不多是2年前。看到dbortz在12天前发布了他过时的解决方案让我意识到这个问题仍然存在混淆,所以我想确保发布正确的解决方案。
对于不使用嵌套表单的备用解决方案,请参阅我的博文:http://www.moncefbelyamani.com/rails-prevent-the-destruction-of-child-object-when-parent-requires-its-presence/