I have a complex save process (a cyclical reference that needs to be resolved by validating and saving and one model, validating and saving another and then updating the first with an id from the second) that has rollbacks depending on if something bad happened etc.
My models are related in this way:
Account
s have one SiteContact
.Account
s have many Customer
s.SiteContact
s are a type of Customer
.Customer
s belong to an Account
Because of the cyclical reference (i.e. Customer
s are invalid WITHOUT an Account
but an Account
needs a SiteContact
which is a type of Customer
.) I decide to save the Account with a blank id for site contact first, then save the site_contact.
I have code similar to this:
# remove the site contact from the account because its not valid yet.
site_contact = params[:account].delete(:site_contact_attributes)
account = Account.new(params[:account])
# some pseudo code
# start database transaction
# validate account
# if valid then
# save account
# site_contact.account_id = account.id
# validate site_contact
# if valid then
# account.site_contact_id = site_contact.id
# save account
# break out!
#
# else
# rollback transaction
# end
# else
# rollback transaction
# end
if Account.exists?(account)
# everything was good! flash[:notice] and redirect somewhere
else
# something bad happened, go back to the page and display the errors.
end
Is it possible to override the Account#save
or some other method and move all of that code into my controller? I was looking at before_save
and before_create
but I'm worried there might be some unintended recursion since I have to save the account
twice.
Any ideas?
In your controller:
site_contact = SiteContact.new(params[:account].delete(:site_contact_attributes))
account = Account.new(params[:account])
if account.save_nested(site_contact)
# success
else
# error
end
In your Account
model.
class Account < ActiveRecord::Base
def save_nested(site_contact)
Account.transaction do
save!
site_contact.account_id = self.id
site_contact.save!
self.site_contact_id = site_contact.id
save!
return true
end
return false
end
end
The save!
will throw an exception when the validation fails. This will automatically roll back the transaction.