2011-02-28 70 views
38

我有两个模型。
- Parenthas_manyChildren;
- Parentaccepted_nested_attributes_forChildren;嵌套模型和父验证

class Parent < ActiveRecord::Base 
    has_many :children, :dependent => :destroy 
    accepts_nested_attributes_for :children, :allow_destroy => true 
    validates :children, :presence => true 
end 

class Child < ActiveRecord::Base 
    belongs_to :parent 
end 

我使用验证来验证每个父母的子女的存在,所以我不能保存父母没有子女。

parent = Parent.new :name => "Jose" 
parent.save 
#=> false 
parent.children_attributes = [{:name => "Pedro"}, {:name => "Emmy"}] 
parent.save 
#=> true 

验证工作。然后,我们将通过_destroy属性摧毁孩子:

parent.children_attributes = {"0" => {:id => 0, :_destroy => true}} 
parent.save 
#=> true !!! 
parent.reload.children 
#=> [] 

,所以我可以通过嵌套形式和验证将通过销毁所有的孩子。

其实那是因为我通过_delete从我的父母删除子后,孩子的方法仍返回销毁对象之前,我重装了,所以验证通过:

parent.children_attributes = {"0" => {:id => 0, :_destroy => true}} 
parent.save 
#=> true !!! 
parent.children 
#=> #<Child id:1 ...> # It's actually deleted 
parent.reload.children 
#=> [] 

它是错误吗?

是什么问题。问题是修复它的最佳解决方案。我的方法是将before_destroy过滤器添加到Child以检查它是否是最后一个。但它使系统变得复杂。

回答

57

这可能适合你,但我有一种感觉,那里有更好的答案。这听起来像是一个bug。

class Parent 
    validate :must_have_children 

    def must_have_children 
    if children.empty? or children.all? {|child| child.marked_for_destruction? } 
     errors.add(:base, 'Must have at least one child') 
    end 
    end 
end 
+0

但它的更好,然后验证在'Child'侧:)谢谢 – fl00r 2011-02-28 16:45:46

+2

和感谢'marked_for_destruction?' – fl00r 2011-02-28 16:51:06

+0

这种验证方法仍然需要像Rails 3.0.6的 – astjohn 2011-04-21 13:35:42

0

这不是一个错误。 Acording的文档

验证指定的 属性不为空(如对象#空白定义 ?)

validates :children, :presence => true仅仅是相同的。该文档没有说明如果您尝试在关联上使用它会发生什么情况。您应该使用自定义验证使用validate

使用validates_presence_ofhas_many协会呼吁blank?上关联children,这是类Array的对象。由于blank?没有为Array定义,因此它触发了在Rails中捕获的method_missing。通常它会按照你的意愿做,但是我发现它在Rails 3.1rc和Ruby 1.8.7中以一种非常糟糕的方式失败了:它默默地恢复了相关记录的变化。我花了几个小时才知道发生了什么事。

+2

其实这个问题,因为它在儿童开除之前无视儿童的存在。所以我们应该检查孩子是否被“标记为破坏”? – fl00r 2011-05-31 11:06:56