Транзакция для нескольких сохранений и выполняет откат, если какая-либо из моделей недействительна

#ruby-on-rails #transactions #rollback

Вопрос:

Я делаю это приложение в Ruby on Rails, и в основном в моем представлении есть эта форма, которая отправляет вложенную params информацию с несколькими данными, что-то вроде этого:

 "attributes"=>
[<ActionController::Parameters {"id"=>1, "date"=>"2010-10-13"} permitted: true>, 
 <ActionController::Parameters {"id"=>8, "date"=>"2020-12-07"} permitted: true>],
 ... // Imagine there being alot of parameters... } permitted: true>, ...
 

Поэтому я хочу создать модели с этими параметрами, а затем сохранить их в своей базе данных. Однако, пока одна из этих моделей недействительна, я хочу выполнить откат, чтобы ни одна из них не была сохранена в базе данных, а затем я возвращаю некоторое сообщение об ошибке, которое я могу отобразить в своем представлении, чтобы сообщить пользователю, где они ошиблись и т.д.

Мой код выглядит примерно так:

  @params_list = params["attributes"]
 Item.transaction do
      @params_list.each do |item_params|
        @item = Item.new(
            id: item_params["id"],
            date: item_params["date"]
        )

      next if @item.save!

      else 
          raise ActiveRecord::Rollback # Since if it is invalid, it would not be saved to the database
????
Kinda lost at this part already
 

Было интересно, есть ли более быстрый способ сделать это? Правильно ли я это делаю, и если это неправильный дизайн, и я должен изменить, как это работает?

PS, я впервые работаю с ruby on rails, так что я довольно растерян и был бы признателен за помощь!

Ответ №1:

Пока вы используете save! , вам не нужно больше и повышать. save! вызывает ошибку, если запись недействительна, и это приведет к откату транзакции. Это совершенно правильный способ сделать это.

Вам нужно ActiveRecord::RecordInvalid будет где-то спасти файл и отправить пользователю соответствующее сообщение об ошибке.

Ответ №2:

Вы можете передать create! методу массив хэшей.

 Item.transaction do  
  Item.create!([
    { title: 'first title' }, 
    { title: 'second title'} 
  ]) 
end
 

В вашем примере:

 Item.transaction do  
  bulk_params = params["attributes"].map{ |item| item.slice(:id, :date) }
  Item.create!(bulk_params)
end
 

Примечание 1: (!) в конце create вызовет исключение, если создание хотя бы одной записи завершится неудачно, и все операции будут отменены с момента создания операции в блоке перехода.

Примечание 2. Это не массовая операция, и производительность будет такой же, как при создании по одному.

Rails(6 ): Если вы хотите вставить как массовую операцию, вы можете использовать insert_all, но это не приведет к созданию экземпляров каких-либо моделей и не вызовет обратных вызовов или проверок активных записей.

 Item.insert_all([
  { title: 'first title' }, 
  { title: 'second title'} 
])