Reputation: 6178
Why wouldn't this transaction rollback if even one create! fails? It's in a controller if that makes any difference.
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
I'm explicitly sending an array with some bad records. The good ones are getting created and the bad ones aren't but I need the whole thing to rollback if even one fails.
Here's my rspec test (passing a json array of photos) and it's incrementing.
expect { post :create, json }.not_to change(Photo, :count)
Upvotes: 13
Views: 7315
Reputation: 1643
This works as well, without the need to propagate the ActiveRecord::Rollback exception.
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
Upvotes: 0
Reputation: 6178
This was just a testing issue with rspec because you're already executing in a transaction in the test. It's the same nested transaction issue discussed here. In order to get the test to work you need to add…
requires_new: true
to the transaction call.
This fixed the test.
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
Important: Rollbacks will only work if your database engine supports them! For example MySQL with MyISAM doesn't support transactions, while MySQL with Inno DB does support them.
Upvotes: 18
Reputation: 1
You need to put begin outside the block like this
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
Upvotes: -3