Транзакция Rails не откат

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

def process_photos(photos)
  ActiveRecord::Base.transaction do
    begin
      photos.each do |photo|
        Photo.create!(creator_user: @user, buyer: @buyer, url: photo['url'])
      end
    rescue
      raise ActiveRecord::Rollback
    end
  end
end

Я явно отправляю массив с некоторыми плохими записями. Хорошие создаются, а плохие - нет, но мне нужно все, чтобы откат, если даже один не удается.

Здесь мой rspec-тест (передача json-массива фотографий) и его увеличение.

expect { post :create, json }.not_to change(Photo, :count)

Ответ 1

Это была просто проблема тестирования с rspec, потому что вы уже выполняете транзакцию в тесте. Это та же самая проблема вложенных транзакций, которая обсуждалась здесь here. Чтобы тест прошел, вам нужно добавить…

requires_new: true

на вызов транзакции.

Это исправило тест.

def process_photos(photos)
  ActiveRecord::Base.transaction(requires_new: true) do
    begin
      photos.each do |photo|
        Photo.create!(creator_user: @user, buyer: @buyer, url: photo['url'])
      end
    rescue
      raise ActiveRecord::Rollback
    end
  end
end

Важно: Откаты будут работать только в том случае, если ваша база данных их поддерживает! Например, MySQL с MyISAM не поддерживает транзакции, а MySQL с Inno DB их поддерживает.

Ответ 2

Вам нужно поставить начало вне блока, как это

def process_photos(photos)
 begin
      ActiveRecord::Base.transaction do
          photos.each do |photo|
            Photo.create!(creator_user: @user, buyer: @buyer, url: photo['url'])
          end
        rescue
          raise ActiveRecord::Rollback
        end
      end
    end

Ответ 3

Это также работает, без необходимости распространять исключение ActiveRecord :: Rollback.

def process_photos(photos)
  begin
    ActiveRecord::Base.transaction do
      photos.each do |photo|
        Photo.create!(creator_user: @user, buyer: @buyer, url: photo['url'])
      end
    end
  rescue ActiveRecord::RecordInvalid
    puts 'Transaction failed'
  end
end