Rails двунаправленный acceptpts_nested_attributes_for вызывает двойное сохранение и потерю грязных атрибутов - PullRequest
0 голосов
/ 13 марта 2019

Допустим, у меня есть 2 модели следующим образом:

class User < ActiveRecord::Base
  has_many :tasks, inverse_of: :user
  accepts_nested_attributes_for :tasks

  before_save :test
  after_save :test2

  def test
    puts "before save #{changes}"
  end

  def test2
    puts "after save #{saved_changes}"
  end
end

class Task < ActiveRecord::Base
  belongs_to :user, inverse_of: :tasks
  accepts_nested_attributes_for :user
end

После запуска этого:

u = User.new(name: 'def')
u.tasks_attributes = [{ name: 'abc' }]
u.save!

Результат:

before save {"name"=>[nil, "def"]}
D, [2019-03-13T08:16:27.972711 #53578] DEBUG -- :    (0.1ms)  begin transaction
D, [2019-03-13T08:16:27.973374 #53578] DEBUG -- :   User Create (0.2ms)  INSERT INTO "users" ("name") VALUES (?)  [["name", "def"]]
before save {}
after save {}
D, [2019-03-13T08:16:27.974821 #53578] DEBUG -- :   Task Create (0.2ms)  INSERT INTO "tasks" ("user_id", "name") VALUES (?, ?)  [["user_id", 1], ["name", "abc"]]
after save {}

Как вы можетевидите, автосохранение заставляет пользователя повторно сохраняться из задачи, даже если именно пользователь создает задачу в первую очередь, что приводит к потере изменений атрибутов.

Тот же код, гдемы удаляем acceptpts_nested_attributes_for из модели задачи, мы получаем ожидаемый результат:

before save {"name"=>[nil, "def"]}
D, [2019-03-13T08:50:20.629287 #54423] DEBUG -- :    (0.1ms)  begin transaction
D, [2019-03-13T08:50:20.629689 #54423] DEBUG -- :   User Create (0.2ms)  INSERT INTO "users" ("name") VALUES (?)  [["name", "def"]]
D, [2019-03-13T08:50:20.630990 #54423] DEBUG -- :   Task Create (0.2ms)  INSERT INTO "tasks" ("user_id", "name") VALUES (?, ?)  [["user_id", 1], ["name", "abc"]]
after save {"id"=>[nil, 1], "name"=>[nil, "def"]}
D, [2019-03-13T08:50:20.631626 #54423] DEBUG -- :    (0.1ms)  commit transaction

Я проследил это до того факта, что автосохранение всегда верно, когда acceptpts_nested_attributes_for присутствует, и эта строка здесь:

saved = record.save(validate: !autosave) if record.new_record? || (autosave && record.changed_for_autosave?)

https://github.com/rails/rails/blob/master/activerecord/lib/active_record/autosave_association.rb#L479

Есть ли способ обойти это?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...