【问题标题】:accepts_nested_attributes_for child association validation failingAccepts_nested_attributes_for 子关联验证失败
【发布时间】:2010-10-30 10:57:03
【问题描述】:

我在我的一个 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 似乎遵循以下模式:

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

模式应该是:

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

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

有什么想法吗?

【问题讨论】:

    标签: ruby-on-rails validation activerecord nested


    【解决方案1】:

    使用: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

    【讨论】:

    • Rails 3.2.3 的未定义方法 `validate_presence_of'。该行应该是 validates_presence_of :dungeon。
    • 它在 Rails 3.1.4 上对我不起作用。即使子模型无效且未保存,也会保存父模型。
    • 这应该是公认的答案,因为另一个只是一种解决方法。
    • 这是什么原因?
    • 我想再次投票赞成这个答案,因为它再次拯救了我。
    【解决方案2】:

    将此答案用于 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
    

    【讨论】:

    • 这对我不起作用。在 validates_presence_of :project_id 调用 'project' 返回 nil 导致它尝试验证 project_id 并且验证失败。我创建了另一个问题,因为我认为问题不同但似乎相同 stackoverflow.com/questions/2102724/…
    • 下面的 inverse_of 解决方案对于 Rails 3.0 及更高版本更正确。
    【解决方案3】:

    只验证关系,不验证 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
    

    【讨论】:

      【解决方案4】:

      不幸的是,以上建议都不适用于 Rails 2.3.5。

      在我的例子中,如果两个项目都是使用嵌套属性创建的,那么任务中的项目总是 nil。只有当我删除 validates_presence_of 时,创建才会成功。单元测试和日志显示一切都已正确创建。

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

      【讨论】:

        【解决方案5】:

        您可以只创建项目并仅在通过验证时添加项目:

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

        【讨论】:

          【解决方案6】:

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

          您描述的问题将在 Rails 3.0 中修复。我会发布指向 Lighthouse 票证的链接,但 stackoverflow.com 不允许这样做,因为我是新用户 (#fail)。不过暂时你可以使用插件“parental_control”,它会修复你的“bug”。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2011-10-04
            • 1970-01-01
            • 2011-01-07
            • 1970-01-01
            • 2017-06-06
            相关资源
            最近更新 更多