问题
In ActiveRecord model after_save callback I need to ROLLBACK transaction and return false.
def after_save_callback
if mycondition?
raise ActiveRecord::Rollback
end
end
This callback rolls-back transaction but mymodel.save! returns true. How to make it return false and rollback?
回答1:
I don't think you can do this with after_save
You should be looking at around_save
instead:
def around_save
ActiveRecord::Base.transaction do
yield # calls the actual save method
raise ActiveRecord::Rollback if my_condition?
end
end
回答2:
If you want to abort a save in an after_save
callback, you should
raise ActiveRecord::RecordInvalid.new(self)
rather than
raise ActiveRecord::Rollback
This will not only roll back the transaction (callbacks always happen inside a possibly-implicit transaction as part of the save
or create
) but also cause save
to return false
.
Here is an article with more details: http://tech.taskrabbit.com/blog/2013/05/23/rollback-after-save/
回答3:
def around_save
ActiveRecord::Base.transaction do
raise ActiveRecord::Rollback # this will actually ROLLBACK
yield # calls the actual save method
raise ActiveRecord::Rollback # this will cause a COMMIT!!! because it affect only this internal transaction.
# OTHER ACTIONS NOT EXECUTED BUT BEING A INTERNAL TRANSACTION, THE PARENT WILL COMMIT, because parent hasn't failed.
end
end
So... I think around_save come already on a transaction block, so you don't need to add that extra ActiveRecord::Base.transaction do block because rollbacks doesnt propagate up
So if you want to rollback before or after yield, you need to remove that internal transaction.
def around_save
#ActiveRecord::Base.transaction do
raise ActiveRecord::Rollback # this will actually ROLLBACK
yield # calls the actual save method
raise ActiveRecord::Rollback # this will actually ROLLBACK
# end
end
EDIT: Reading what I wrote... now seem difficult to understand. The point is: if you are gonna use aroud_save
don't wrapp again with ActiveRecord::Base.transaction
(do like in the last example) because rails will wrap the call to around_save
with is own ActiveRecord::Base.transaction
so when you raise ActiveRecord::Rollback
you are only rolling back the most internal transaction, so you can end with extrange results and partial saves (like in the first example whic is FAIL).
来源:https://stackoverflow.com/questions/9876465/return-false-and-rollback-in-after-save-callback