Как спасти транзакцию модели и показать пользователю ошибку?
Итак, представьте, что у вас есть 2 модели, Person и Address, и только один адрес на человека может быть помечен как "Основной". Поэтому, если я хочу изменить основной адрес человека, мне нужно использовать транзакцию, чтобы отметить новый как основной и снять отметку с прежнего. И насколько я знаю, использование транзакций в контроллерах не очень хорошо, поэтому у меня есть специальный метод в модели, вот что у меня есть:
AddressesController < ApplicationController
def update
@new_address = Address.find(params[:id])
@old_address = Address.find(params[:id2])
@new_address.exchange_status_with(@old_address)
end
end
Модель:
class Address < ActiveRecord::Base
def exchange_status_with(address)
ActiveRecord::Base.transaction do
self.save!
address.save!
end
end
end
Таким образом, если транзакция в методе модели завершается неудачно, мне нужно ее спасти и сообщить пользователю об ошибке, как мне это сделать? Есть ли способ заставить этот метод модели возвращать true или false в зависимости от успешности транзакции или нет, например, метод save?
Вероятно, я мог бы поместить эту транзакцию в контроллер и отобразить сообщение об ошибке в части спасения, но я думаю, что это неправильно, или я мог бы применить этот метод в обратном вызове, но представьте, что есть какая-то причина, почему я не могу этого сделать, Какая альтернатива?
PS не обращайте внимания на поиск экземпляров с параметрами id и id2, просто случайность, чтобы показать, что у меня есть 2 экземпляра
Ответы
Ответ 1
def exchange_status_with(address)
ActiveRecord::Base.transaction do
self.save!
address.save!
end
rescue ActiveRecord::RecordInvalid => exception
# do something with exception here
end
FYI, исключение выглядит следующим образом:
#<ActiveRecord::RecordInvalid: Validation failed: Email can't be blank>
и
exception.message
# => "Validation failed: Email can't be blank"
Обратите внимание: вы можете изменить self.save!
на save!
Альтернативное решение, если вы хотите сохранить свои активные ошибки модели:
class MyCustomErrorClass < StandardError; end
def exchange_status_with(address)
ActiveRecord::Base.transaction do
raise MyCustomErrorClass unless self.save
raise MyCustomErrorClass unless address.save
end
rescue MyCustomErrorClass
# here you have to check self.errors OR address.errors
end