Это распространенная проблема, с которой люди сталкиваются с ActiveRecord.
Проблема
Метод установки location_ids=
немедленно добавит / обновит / удалит записи, о которых говорится в Справочник ассоциаций ActiveRecord .Такое поведение часто удивляет разработчиков и часто нежелательно.Я почти всегда избегаю этого.
В своем коде вы сначала вызываете assign_attributes
, что, в свою очередь, вызывает location_ids=
.Изменения немедленно сохраняются в записях.Впоследствии, когда вызывается save!
, открывается новая транзакция.Если возникает ошибка проверки, тогда будут отменены только изменения в этой транзакции, которые исключают уже сохраненные изменения, сделанные location_ids=
.
@customer.assign_attributes(params...) # location_ids are saved outside of the `save!` transaction.
@customer.save! # validation errors will cause a rollback only to this point, excluding changes from the previous line.
Простое решение
Использованиеupdate_attributes!
для замены assign_attributes
и save!
.Это приведет к тому, что все изменения в транзакции будут перенесены так, что откат отменит все, что вы хотите.Yay!
@customer.update_attributes!(params.require(:customer).permit(:first_name, :middle_initial, :last_name, :location_ids => []))
Альтернативный подход
Иногда может быть невозможно избежать отдельных вызовов на номера assign_attributes
и save
.Это делает вещи намного сложнее.Я не могу придумать ни одного варианта для этого случая.
Одним из возможных решений является использование вложенных атрибутов для обновления / уничтожения дочерних записей.
location_ids
действительно ярлык для обновлениякаждая запись местоположения связана с данным клиентом.Вместо того, чтобы полагаться на это, вы можете использовать вложенные атрибуты в ваших формах для обновления местоположений.Этот метод может использовать функцию mark_for_destruction
( link ) для автосохранения.Описание полного решения для этого подхода было бы чрезмерно длительным, но оно оказалось очень эффективным для меня.