【问题标题】:Rails accepts_nested_attributes_for validation on transactional objectRails 接受_nested_attributes_for 验证事务对象
【发布时间】:2013-04-23 10:23:20
【问题描述】:

为了使嵌套属性的验证在我的 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 测试我的示例代码并报告它正在工作,这为我指明了正确的方向来解决这个问题。

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

【问题讨论】:

  • 您的代码中几乎没有错误。可能是错字。 1. '子值不能为空' -- 单引号内的单引号。 2. && parent.status = 'validate_children' -- 这不是比较。它的任务
  • 抱歉有错别字!这里的代码只是我的应用程序的一个简化示例,所以这不是它不起作用的原因,但感谢您指出这一点:)

标签: ruby-on-rails validation nested


【解决方案1】:

我认为您应该为此使用validates_associated

在 Child 中进行以下验证

validates :value, :presence => true, :if => "self.parent.status == 'validate_children'"

【讨论】:

  • 您好 Santhosh,感谢您的快速回答!我已经进入了 validates_associated 但这里的一般感觉是它比任何事情都更麻烦。有没有一种方法可以仅从父模型验证关联记录属性而不触及他们自己的模型?这将是必需的,因为在我的设置中,孩子可以在没有值的情况下存在于数据库中,但前提是父母的状态与“validate_children”不同。
  • 在这种情况下,您可以像这样在子项中添加验证:validates :value, :presence => true, :if => "self.parent.status == 'validate_children'"
  • 嘿 Santhosh,再次感谢您的回复。问题是我的应用程序比示例复杂一些,并且它还涉及多态关联,因此将验证保持在父级会更好。我刚刚发现验证适用于一级嵌套模型,但它们不适用于深层嵌套模型。例如。如果孩子有描述,验证将适用于孩子,但不适用于他们的描述。
  • 我用 Post-Comment 模型尝试了你的代码,它对我有用。 (我已将 errors.add[:base, msg] 更改为 errors.add(:base, msg) )
  • 感谢您的帮助 Santhosh,我仍然无法让它在我身边工作,不幸的是代码比示例复杂得多,这使得在这里复制粘贴变得困难.目前,我将第一级验证保留在模型中,并将深层嵌套的验证移至控制器,如果第一个验证通过,我必须首先保存记录,然后检查更深的验证。我会尝试一个更简单的版本,看看它是否能像你一样工作。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-02-03
  • 1970-01-01
  • 1970-01-01
  • 2011-03-04
  • 2013-12-24
  • 2023-04-10
  • 1970-01-01
相关资源
最近更新 更多