Mark Reginald J. wrote:
Michael K. wrote:
Super! Thanks so much. It worked perfectly. The only thing I changed
from your example was to handle the update as a transaction. Updating
@registration and @customer with the “.attributes” method did the trick
perfectly. I am off to experiment with the “validates_associated
:customer”.
Could you explain a little more what you changed and why.
I will do my best; it certainly helps to re-enforce what I’ve learned.
In your example you provided, what was to me, a novel idea in terms of
updating the @snowplow_registration and @customer objects. When these
objects were first created in the update method, they had the properties
of a newly fetched record from the database. It wasn’t until I called
on each of these:
@snowplow_registration.update_attributes!(params[:snowplow_registration])
@customer.update_attributes!(params[:customer])
respectively, did the newly created objects, instantiated at the
beginning, take on the properties of my submitted form. My problem was
that when the transaction failed during the first update, I would never
see the newly created @customer object updated with my form information.
By calling the “.attributes = params[]” method on each of these BEFORE I
started my transaction I now had in memory the new form data I entered
to pass to my rescue catch. In your code, you check the Customer object
before submitting it to be written.
I was about to write about how important it was for me to do this update
in a transaction because of the possibility of the database failing in
between writes and how your code didn’t account for that. I glanced
over your code when I saw:
if @snowplow_registration.valid? && customer_valid
and simply assumed you were doing a standard non-transactional save to
the database. Now that I’m inspecting this more closely, I see that you
did-in-fact keep this a transactional operation. Here is the code I am
using now:
def update
@snowplow_registration = SnowplowRegistration.find(params[:id])
@customer = @snowplow_registration.customer
@snowplow_registration.attributes = params[:snowplow_registration]
@customer.attributes = params[:customer]
SnowplowRegistration.transaction do
@snowplow_registration.update_attributes!(params[:snowplow_registration])
@customer.update_attributes!(params[:customer])
flash[:notice] = 'Snow Plow Registration was successfully
updated.’
redirect_to(@snowplow_registration)
end
rescue ActiveRecord::RecordInvalid => e
@customer.valid?
render :action => “edit”
end
As you can see the only changes I’ve made from my first post are:
-
I originally had in my 3rd line:
@customer = Customer.find(@snowplow_registration.customer)
and changed it to what you had:
@customer = @snowplow_registration.customer
I thought your way looked cleaner.
-
I then added:
@snowplow_registration.attributes = params[:snowplow_registration]
@customer.attributes = params[:customer]
as I explained above, this did the trick to update BEFORE
my transaction occurred, the @customer and @registration classes.
I had to change nothing else from my original example as the checks are
already in place.
One little improvement: Use save_without_validation! to prevent
the validations from being run twice.
Another good thought, however what I am using now doesn’t have the same
duplication as your suggested code does. Unless of course when I call
the “attributes” method it runs a check then as well. I’m not sure that
is the case. I’ll have to think about that.
Thank you so much for this discussion and all the wonderful new tidbits
you’ve showed me. Like I said above, this discussion just helps
reinforce the things that I am learning.