Допустим, у меня есть 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
Есть ли способ обойти это?