Rails ActiveRecord:保存嵌套模型回滚 [英] Rails ActiveRecord: Saving nested models is rolled back

查看:76
本文介绍了Rails ActiveRecord:保存嵌套模型回滚的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

使用Rails 5:

gem 'rails', '~> 5.0.0', '>= 5.0.0.1'

我创建了最简单的示例可以想到来证明这个问题:

I've created the simplest example I can think of to demonstrate the issue:

parent.rb

class Parent < ApplicationRecord
  has_many :children
  accepts_nested_attributes_for :children
end

child.rb

class Child < ApplicationRecord
  belongs_to :parent
end

创建父级,保存,创建孩子,保存(工作)

使用 rails控制台,创建一个新的父对象,然后保存,然后从父母那里生一个孩子,然后保存父母,就可以了:

Using rails console, creating a new parent, then saving, then building a child from the parent, then saving the parent, works fine:

irb(main):004:0> parent = Parent.new
=> #<Parent id: nil, created_at: nil, updated_at: nil>
irb(main):005:0> parent.save
   (0.5ms)  BEGIN
  SQL (0.4ms)  INSERT INTO `parents` (`created_at`, `updated_at`) VALUES ('2016-09-25 13:05:44', '2016-09-25 13:05:44')
   (3.2ms)  COMMIT
=> true
irb(main):006:0> parent.children.build
=> #<Child id: nil, parent_id: 1, created_at: nil, updated_at: nil>
irb(main):007:0> parent.save
   (0.5ms)  BEGIN
  Parent Load (0.5ms)  SELECT  `parents`.* FROM `parents` WHERE `parents`.`id` = 1 LIMIT 1
  SQL (0.7ms)  INSERT INTO `children` (`parent_id`, `created_at`, `updated_at`) VALUES (1, '2016-09-25 13:05:52', '2016-09-25 13:05:52')
   (1.3ms)  COMMIT
=> true

创建父母,创建孩子,保存(无效)

但是,如果我尝试创建新的父级,则在不保存父级的情况下构建子级 ,最后将父级保存起来,则交易失败并回滚:

However, if I try to create a new parent, then build the child without saving the parent, and finally save the parent at the end, the transaction fails and is rolled back:

irb(main):008:0> parent = Parent.new
=> #<Parent id: nil, created_at: nil, updated_at: nil>
irb(main):009:0> parent.children.build
=> #<Child id: nil, parent_id: nil, created_at: nil, updated_at: nil>
irb(main):010:0> parent.save
   (0.5ms)  BEGIN
   (0.4ms)  ROLLBACK
=> false

有人可以解释原因以及如何解决吗?

Can anyone explain why, and how to fix?

更新

同时创建父母和子女,然后通过 validate进行保存是可行的:false ,所以这表明问题是对子项的验证失败,因为它需要设置parent_id-但是大概子项验证必须在保存父项之前那么,还是不会失败?

Creating both parent and child, then saving does work if you pass validate: false, so this points to the issue being validation of the child failing, because it requires the parent_id to be set - but presumably the child validation must be running before the parent is saved then, or it wouldn't fail?

irb(main):001:0> parent = Parent.new
=> #<Parent id: nil, created_at: nil, updated_at: nil>
irb(main):002:0> parent.children.build
=> #<Child id: nil, parent_id: nil, created_at: nil, updated_at: nil>
irb(main):003:0> parent.save(validate: false)
   (0.7ms)  BEGIN
  SQL (0.9ms)  INSERT INTO `parents` (`created_at`, `updated_at`) VALUES ('2016-09-25 15:02:20', '2016-09-25 15:02:20')
  SQL (0.8ms)  INSERT INTO `children` (`parent_id`, `created_at`, `updated_at`) VALUES (3, '2016-09-25 15:02:20', '2016-09-25 15:02:20')
   (1.6ms)  COMMIT
=> true

更新2

如果我删除了<$ c $,它也可以使用保存(没有验证:false )工作。 child.rb 中的c> belongs_to:parent 行,因为此后没有验证 parent_id 在被保留之前是有效的-但是,您将失去从孩子那里找父母的能力(通过 child.parent )。您仍然可以从父母那里找到孩子(通过 parent.child )。

It also works using save (without the validation: false) if I remove the belongs_to :parent line from child.rb, since then no validation takes place that parent_id is valid before being persisted - however, then you lose ability to get at the parent from the child (via child.parent). You can still get to the child from the parent (via parent.child).

推荐答案

尝试以下操作:

class Child < ApplicationRecord
  belongs_to :parent, optional: true
end

经过一些研究我发现Rails 5现在要求在子级默认情况下中存在关联的ID。否则,Rails会触发验证错误。

After doing some research I discovered that Rails 5 now requires an associated id to be present in the child by default. Otherwise Rails triggers a validation error.

查看此文章进行了很好的解释,并提出了相关的拉出请求

Check out this article for a great explanation and the relevant pull request

...以及官方的Rails 指南对其进行非常简短的提及:

...and the official Rails guide make a very brief mention of it:


4.1.2.11:optional

4.1.2.11 :optional

如果将:optional选项设置为true,则
关联对象的存在将'待验证。默认情况下,此选项将
设置为false。

If you set the :optional option to true, then the presence of the associated object won't be validated. By default, this option is set to false.

因此您可以通过添加<$ c来关闭此新行为$ c>可选: belongs_to 对象之后的true 。

So you can turn off this new behavior by adding optional: true after the belongs_to object.

所以在您的示例中您必须先创建/保存父级,然后再生成子级,或使用可选:true

So in your example you would have to create/save Parent first before building the child, or use optional: true

这篇关于Rails ActiveRecord:保存嵌套模型回滚的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆