I have an enrollment form.
When the user enrolls, the app is supposed to save the data in the enrollments
table and in the users
table. (I need this separation because the user's profile can change but the data he entered for that particular enrollment has to be archived. So even if later the user changes his last name, in the enrollment form I'll have his initial information.)
So I was thinking about saving data in the enrollments
table then have a after_create call, like this...
class Enrollment < ActiveRecord::Base
after_create :save_corresponding_user
def save_corresponding_user
user = User.new
user.full_name = self.user_full_name
user.email = self.user_email
user.mobile_phone = self.user_mobile_phone
user.save
end
end
The issue is, what if saving the user fails for any reason. How can I rollback and destroy the just saved data from the enrollments
table?
Returning false
from after_create
will do nothing.
The whole callback chain is wrapped in a transaction. If any before callback method returns exactly false or raises an exception, the execution chain gets halted and a ROLLBACK is issued; after callbacks can only accomplish that by raising an exception.
Also, you must raise ActiveRecord::Rollback
:
Any exception that is not ActiveRecord::Rollback will be re-raised by Rails after the callback chain is halted. Raising an exception other than ActiveRecord::Rollback may break code that does not expect methods like save and update_attributes (which normally try to return true or false) to raise an exception.
http://guides.rubyonrails.org/active_record_callbacks.html#halting-execution
I do something like this:
after_create do
if condition
errors.add(:attr, 'Blah blah blah.')
raise ActiveRecord::Rollback
end
end
For Rails 3: http://guides.rubyonrails.org/v3.2.13/active_record_validations_callbacks.html#halting-execution
after_create is a part of the transaction saving the current model. Therefore, if your code crashes or if after_create returns false, it should rollback the current transaction and invalidate the enrollment
saving.
If you want to simulate this, add this to your after_create and see if everything works as expected :
raise Exception.new("CRASH")
As @anthonyalberto mentioned, after_create is already part of the transaction. To define a transaction you would use something like this in your controller:
Enrollment.transaction do
@enrollment.save!
end
That is really all you need to do, if the save of enrollment fails or the save of user fails it will roll back your entire transaction. Here is more information: http://api.rubyonrails.org/classes/ActiveRecord/Transactions/ClassMethods.html
In rails 5, you need to use throw :abort
to rollback ActiveRecord chain.
You can see the list of operation handling in different versions of Rails along with before and after callbacks. For better clarification, please look at the concept of halting executions. Please follow the the given link below.
https://guides.rubyonrails.org/active_record_callbacks.html#halting-execution
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With