为了使嵌套属性的验证在我的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 测试我的示例代码并报告它正在工作,这为我指明了正确的方向来解决这个问题。
为了将来参考,原始问题中的代码适用于这种动态的、深度嵌套的验证。