Rails对事务对象进行Accept_nested_attributes_for验证

时间:2013-04-23 10:23:21

标签: ruby-on-rails validation nested

为了使嵌套属性的验证在我的rails应用程序中运行,我几个小时都在苦苦挣扎。一个小小的警告是,我必须根据父级的属性动态验证嵌套属性,因为所需的信息量会根据父级在进程中的位置随时间而变化。

所以这是我的设置:我有一个父母有很多不同的关联模型,我想在每次保存父项时验证那些嵌套属性。鉴于验证动态变化,我不得不在模型中编写自定义验证方法:

class Parent < ActiveRecord::Base
  attr_accessible :children_attributes, :status
  has_many :children
  accepts_nested_attributes_for :children
  validate :validate_nested_attributes
  def validate_nested_attributes
    children.each do |child|
      child.descriptions.each do |description|
        errors.add(:base, "Child description value cant be blank") if description.value.blank? && parent.status == 'validate_children'
      end
    end
  end
end


class Child < ActiveRecord::Base
  attr_accessible :descriptions_attributes, :status
  has_many :descriptions
  belongs_to :parent
  accepts_nested_attributes_for :descriptions
end

在我的控制器中,当我想保存时,我在父控件上调用update_attributes。现在的问题是,显然,rails运行针对数据库的验证,而不是针对用户或控制器修改的对象。因此,可能发生的情况是用户擦除了孩子的值并且验证将通过,而后来的验证将不会通过,因为数据库中的项目无效。

以下是此方案的快速示例:

parent = Parent.create({:status => 'validate_children', :children_attributes => {0 => {:descriptions_attributes => { 0 => {:value => 'Not blank!'}}}})
  #true
parent.update_attributes({:children_attributes => {0 => {:descriptions_attributes => { 0 => {:value => nil}}}})
  #true!! / since child.value.blank? reads the database and returns false
parent.update_attributes({:children_attributes => {0 => {:descriptions_attributes => { 0 => {:value => 'Not blank!'}}}})
  #false, same reason as above

验证适用于第一级关联,例如如果一个Child有'value'属性,我可以像我一样运行验证。问题在于深层关联,在保存之前显然无法验证。

有人能指出我如何解决这个问题的正确方向吗?我目前看到的唯一方法是保存记录,然后验证它们,如果验证失败则删除/恢复它们,但我老实说希望有更干净的东西。

提前谢谢大家!

事实证明,我通过直接在自定义验证中引用这些模型来运行深层嵌套模型的验证,这样:

class Parent < ActiveRecord::Base
  [...]
  has_many :descriptions, :through => :children
  [...]
  def validate_nested_attributes
    descriptions.each do |description|
      [...]
    end
  end
end

由于某种原因导致我上面遇到的问题。感谢Santosh测试我的示例代码并报告它正在工作,这使我指出了正确的方向来解决这个问题。

为了将来参考,原始问题中的代码适用于这种动态的,深层嵌套的验证。

1 个答案:

答案 0 :(得分:2)

我认为您应该使用validates_associated来实现此目标

在Child

中进行以下验证
validates :value, :presence => true, :if => "self.parent.status == 'validate_children'"