5

为了使嵌套属性的验证在我的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 具有“值”属性,我可以按照我的方式运行验证。问题在于在保存之前显然无法验证的深层关联。

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

谢谢大家!

解决方案

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

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

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

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

4

1 回答 1

2

我认为您应该为此使用validates_associated

在 Child 中进行以下验证

validates :value, :presence => true, :if => "self.parent.status == 'validate_children'"
于 2013-04-23T10:30:27.837 回答