accepted_nested_attributes_for儿童关联validation失败

我在我的一个Rails模型中使用了accep_nested_attributes_for,我想在创build父项之后保存子项。

表单完美,但validation失败。 为了简单起见,想象如下:

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' } ] ) 

在保存项目模型时,validation在任务上失败,因为他们没有project_id(因为项目没有被保存)。

看来Rails正在遵循以下模式:

  • validation项目
  • validation任务
  • 保存项目
  • 保存任务

该模式应该是:

  • validation项目
  • 通过:保存项目并继续…
  • validation任务
    • 通过:保存任务
    • 在失败:删除项目(回滚也许?)

所以我的问题归结为:如何让Rails运行project_id =(或project =)方法和validation子项(任务)后父(项目)已被保存,但不保存父(项目)模型如果有任何小孩(任务)无效?

有任何想法吗?

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 

使用:inverse_ofvalidates_presence_of :parent 。 这应该解决您的validation问题。

  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

只validation关系,而不是ID:

 class Task < ActiveRecord::Base belongs_to :project validates_presence_of :project end 

一旦关联被填充,ActiveRecord就会认为validation已经成功,不pipe模型是否被保存。 您也可能要调查自动保存,以确保任务的项目始终保存:

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

不幸的是,上面的所有build议都不适用于Rails 2.3.5。

在我的情况下,任务中的项目总是为零,如果两者都是使用嵌套属性创build的。 只有当我删除validates_presence_of,创build成功。 unit testing和日志显示所有创build正确。

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

您可以创build项目,只有在通过validation时才添加项目:

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

再见

与bigobuild议的不同,先保存父对象然后再保存子对象并不总是可以接受的。 通常你想在开始保存之前确认所有的对象都是有效的。 这使用户有机会重新编辑input表单并更正任何错误。

你所描述的问题将在Rails 3.0中得到解决。 我会张贴一个链接到灯塔票,但stackoverflow.com不允许这个,因为我是一个新用户(#fail)。 但暂时,你可以使用插件“ parental_control ”,这将修复你的“错误”。