accepts_nested_attributes_for子关联验证失败

时间:2009-06-01 16:43:27

标签: ruby-on-rails validation activerecord nested

我在我的一个Rails模型中使用accepts_nested_attributes_for,我想在创建父项后保存子项。

表单完美无缺,但验证失败。为简单起见,请想象以下内容:

class Project < ActiveRecord::Base
  has_many :tasks
  accepts_nested_attributes_for :tasks
end

class Task < ActiveRecord::Base
  belongs_to :project

  validates_presence_of :project_id
  validates_associated :project
end

我正在跑步:

Project.create!(
  :name => 'Something',
  :task_attributes => [ { :name => '123' }, { :name => '456' } ]
)

保存项目模型后,验证失败,因为它们没有project_id(因为项目尚未保存)。

似乎Rails遵循以下模式:

  • 验证项目
  • 验证任务
  • 保存项目
  • 保存任务

模式应为:

  • 验证项目
  • On Pass:保存项目并继续......
  • 验证任务
    • 通过:保存任务
    • 失败:删除项目(可能是回滚?)

所以我的问题归结为:如何在保存父项目(项目)之后让Rails运行project_id =(或project =)方法并对子项(任务)进行验证,但不保存父项( project)模型,如果任何子(任务)无效?

有什么想法吗?

6 个答案:

答案 0 :(得分:161)

使用:inverse_ofvalidates_presence_of :parent。这应该可以解决您的验证问题。

   class Dungeon < ActiveRecord::Base
     has_many :traps, :inverse_of => :dungeon
   end

   class Trap < ActiveRecord::Base
     belongs_to :dungeon, :inverse_of => :traps
     validates_presence_of :dungeon
   end

http://apidock.com/rails/ActiveModel/Validations/HelperMethods/validates_presence_of

https://github.com/rails/rails/blob/73f2d37505025a446bb5314a090f412d0fceb8ca/activerecord/test/cases/nested_attributes_test.rb

答案 1 :(得分:12)

将此答案用于Rails 2,否则请参阅下面的:inverse_of答案

如果相关项目有效,您可以通过检查project_id来解决此问题。


class Task < ActiveRecord::Base
  belongs_to :project

  validates_presence_of :project_id, :unless => lambda {|task| task.project.try(:valid?)}
  validates_associated :project
end

答案 2 :(得分:9)

仅验证关系,而不是ID:

class Task < ActiveRecord::Base
  belongs_to :project

  validates_presence_of :project
end

一旦填充了关联,ActiveRecord就会认为验证成功,无论模型是否已保存。您可能还想调查自动保存,以确保始终保存任务的项目:

class Task < ActiveRecord::Base
  belongs_to :project, :autosave => true

  validates_presence_of :project
end

答案 3 :(得分:2)

不幸的是,上面的建议对Rails 2.3.5都没有用。

在我的例子中,如果两个都是使用嵌套属性创建的,那么任务中的项目总是为零。只有删除validates_presence_of,才能成功完成创建。单元测试和日志显示所有内容都已正确创建。

所以我现在倾向于向数据库添加约束而不是Rails,因为这似乎首先更可靠。

答案 4 :(得分:1)

您可以创建项目,只有在通过验证时才添加项目:

tasks = params.delete(:task_attributes)
if Project.create(params)
  Project.update_attributes(:task_attributes => tasks)
end

答案 5 :(得分:0)

与bigo建议的相反,首先保存父对象然后保存子对象并不总是可以接受的。通常,您希望在开始保存之前确保所有对象都已验证。这使用户有机会重新编辑输入表单并更正任何错误。

您描述的问题将在Rails 3.0中修复。我会发布一个灯塔票的链接,但stackoverflow.com不允许这样,因为我是一个新用户(#fail)。但目前,您可以使用插件“parental_control”来修复您的“错误”。