Rails ActiveRecord: Salvar modelos aninhados é revertido
Usando o Rails 5:
gem 'rails', '~> 5.0.0', '>= 5.0.0.1'
Eu criei o exemplo mais simples em que posso pensar para demonstrar o problema:
parent.rb
class Parent < ApplicationRecord
has_many :children
accepts_nested_attributes_for :children
end
child.rb
class Child < ApplicationRecord
belongs_to :parent
end
Criar pai, salvar, criar filho, salvar (funciona)
Usandorails console
, criando um novo pai, salvando, criando um filho a partir do pai e salvando o pai, funciona bem:
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
Criar pai, filho, salvar (não funciona)
No entanto, se eu tentar criar um novo pai, crie o filhosem salvando o pai e, finalmente, salvando o pai no final, a transação falha e é revertida:
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
Alguém pode explicar o porquê e como corrigir?
ATUALIZAR
Criando pai e filho, o salvamento funciona se você passarvalidate: false
, portanto, isso indica que o problema está na validação do filho com falha, porque exige que o parent_id seja definido - mas, presumivelmente, a validação do filho deve estar em execuçãoantes o pai é salvo então, ou não falharia?
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
ATUALIZAÇÃO 2
Também funciona usandosave
(sem ovalidation: false
) se eu remover obelongs_to :parent
linha dechild.rb
, desde então, nenhuma validação ocorreparent_id
é válido antes de persistir - no entanto, você perde a capacidade de chegar ao pai da criança (viachild.parent
) Você ainda pode chegar ao filho pelos pais (viaparent.child
)