How to update parent of a belongs_to association

I have a pair of models:

class Document < ActiveRecord::Base
has_many :revisions

attributes: Title, Number, etc…

end

Class Revision < ActiveRecord::Base
belongs_to :Document

attributes: document_id, revision, release_status, etc…

end

I have a controller & view for the Revision model (note, this is the
model that belongs to the Document model).
the “edit” view looks something like this:
<% form_for(@revision) do |f| %>
<% fields_for(@revision.number) do |r| %>
<%= r.text_field :title %>
<%= r.text_field :number %>

When my “update” method gets called, how can I update the
corresponding “revision.number” record simultaneously with updating my
“revision” record?

I know I can do something like this:
success = @revision.number.update_attributes(params[:number])
success &= @revision.update_attributes(params[:revision])

but, what happens if the second call to “update_attributes” fails? I
will have updated the Number model, but not the Revision model.

Is there an idiomatic way to handle this?

I suppose I could rethink my application, and write a documents
controller & view and update the revision as part of updating the
document. But I would like to know how to update the parent of a
belongs_to association regardless.

Thanks for your advice and enlightenment.

–wpd

Document.transaction do

update the revision

update the document

end

If one fails, you rollback the transaction and you’re done; no
half-modified record pairs.

Brent

Patrick D. wrote:

I have a pair of models:

class Document < ActiveRecord::Base
has_many :revisions

attributes: Title, Number, etc…

end

Class Revision < ActiveRecord::Base
belongs_to :Document

attributes: document_id, revision, release_status, etc…

end

I have a controller & view for the Revision model (note, this is the
model that belongs to the Document model).
the “edit” view looks something like this:
<% form_for(@revision) do |f| %>
<% fields_for(@revision.number) do |r| %>
<%= r.text_field :title %>
<%= r.text_field :number %>

When my “update” method gets called, how can I update the
corresponding “revision.number” record simultaneously with updating my
“revision” record?

I know I can do something like this:
success = @revision.number.update_attributes(params[:number])
success &= @revision.update_attributes(params[:revision])

but, what happens if the second call to “update_attributes” fails? I
will have updated the Number model, but not the Revision model.

Is there an idiomatic way to handle this?

I suppose I could rethink my application, and write a documents
controller & view and update the revision as part of updating the
document. But I would like to know how to update the parent of a
belongs_to association regardless.

Thanks for your advice and enlightenment.

–wpd

Let’s say that we wanted to do the same thing, only as part of a
plugin. For instance, say we are creating a plugin called
acts_as_cached that causes a set of fields in a shadow model to be
updated whenever the target model is created/updated. The natural
implementation is to locate or create the shadow record and update the
fields using the appropriate callback. The problem with using
transactions in this case is that there don’t seem to be any good ways
to specify or finalize the transaction with callbacks. Without
transactions, there is no guarantee that things will get saved or
recovered on errors. With transactions, there is no way to start and
finish the transaction in the same callback but still save the both
records at the right spot in the callback chain.

Note that observers won’t work – they aren’t transactional.

The only thing I can think of is to alias wrap the target class state-
changing methods (create, update, destroy) and put the transaction
code in there, in turn calling the previous CUD operations inside a
transaction.

Any ideas?

-ns

You’re right that you should use the update_attributes! bang method,
because it will fail with an exception. Throwing an exception inside a
transaction block causes the transaction to rollback. That’s how you
cause a rollback to happen. You should still rescue the exception and
handle it somehow, at the very least by presenting an error message to
the user.

Transactions are still very much supported, and actually we may be
getting nested transactions soon, which would be a boon for testing:
http://rails.lighthouseapp.com/projects/8994/tickets/383-activerecord-should-use-savepoints-for-nested-transactions

Good luck!

Patrick D. wrote:

On Sun, Jun 22, 2008 at 1:49 AM, Brent M.
[email protected] wrote:

Document.transaction do

update the revision

update the document

end

If one fails, you rollback the transaction and you’re done; no
half-modified record pairs.
Thanks…
I thought about using a transaction, but couldn’t find any description
of it in my “The Rails Way” book – at least it’s not in the index
anywhere, and I couldn’t find it in the ActiveRecord chapters I
skimmed.

I was a little concerned that a transaction seemed to be specific to a
single model (the “Document.transaction” part scared me.) But I now
see in the API that the records need not all be instances of that
class.

Looking at the API, all of the examples use the #save! method. Should
I be concerned that I am using the #update_attributes method? Or
should I presume that it does the right thing. Hmmm… looking more
carefully, I see an #update_attributes! method that will throw an
exception if the update fails (actually, if the #save! fails). I’m
not sure what you mean by “you rollback the transaction”. It seems to
me that the rollback would occur automatically if something bad, say
an exception, occurred. Do I have the gist of this right? Or is
there something else I need to do to roll it back?

Finally, I have this niggling suspicion that I read somewhere that the
use of transactions was deprecated, but I could be confusing that with
something else.

Again, thanks for pointing me in a promising direction.

–wpd

On Sun, Jun 22, 2008 at 1:49 AM, Brent M.
[email protected] wrote:

Document.transaction do

update the revision

update the document

end

If one fails, you rollback the transaction and you’re done; no
half-modified record pairs.
Thanks…
I thought about using a transaction, but couldn’t find any description
of it in my “The Rails Way” book – at least it’s not in the index
anywhere, and I couldn’t find it in the ActiveRecord chapters I
skimmed.

I was a little concerned that a transaction seemed to be specific to a
single model (the “Document.transaction” part scared me.) But I now
see in the API that the records need not all be instances of that
class.

Looking at the API, all of the examples use the #save! method. Should
I be concerned that I am using the #update_attributes method? Or
should I presume that it does the right thing. Hmmm… looking more
carefully, I see an #update_attributes! method that will throw an
exception if the update fails (actually, if the #save! fails). I’m
not sure what you mean by “you rollback the transaction”. It seems to
me that the rollback would occur automatically if something bad, say
an exception, occurred. Do I have the gist of this right? Or is
there something else I need to do to roll it back?

Finally, I have this niggling suspicion that I read somewhere that the
use of transactions was deprecated, but I could be confusing that with
something else.

Again, thanks for pointing me in a promising direction.

–wpd

Fabulous!
Thank you very much.

–wpd

On 23 Jun 2008, at 00:33, Patrick D. wrote:

half-modified record pairs.

Document.transaction is just shorthand for
Document.connection.transaction: the only time the class upon which
you call transaction is relevant is when your model classes are spread
across multiple databases and you need to be sure that you’re creating
transactions in the right one. If you don’t like looking like you’re
favouring one model you could always use ActiveRecord::Base.transaction

Finally, I have this niggling suspicion that I read somewhere that the
use of transactions was deprecated, but I could be confusing that with
something else.

What was deprecated (or even removed, can’t remember) was object
transactions, ie a rollback triggering the rollback of a ruby object
(instance variables etc…)

Fred

On Mon, Jun 23, 2008 at 5:35 AM, Frederick C.
[email protected] wrote:

Document.transaction is just shorthand for
Document.connection.transaction: the only time the class upon which
you call transaction is relevant is when your model classes are spread
across multiple databases and you need to be sure that you’re creating
transactions in the right one. If you don’t like looking like you’re
favouring one model you could always use ActiveRecord::Base.transaction

Which is more common in the community?

MyModel.transaction

or

ActiveRecord::Base.transaction

One of the challenges of learning to speak any foreign language is
learning the idioms, because the literal translation seems strange in
one’s native tongue. Hence the use of “MyModel.transaction” feels
strange to me, but if that is the idiom that is typically used (and I
suspect it is), then I can start using it as well and gain slightly
more fluency in this “RoR” language and the culture I am (very slowly)
joining.

Finally, I have this niggling suspicion that I read somewhere that the
use of transactions was deprecated, but I could be confusing that with
something else.

What was deprecated (or even removed, can’t remember) was object
transactions, ie a rollback triggering the rollback of a ruby object
(instance variables etc…)
Ahhh… now that I read that, I do seem to recall seeing the word
“object” in front of the word “transaction”. OK, I’ll use
transactions in the full confidence that they are the right way to
solve this sort of problem.

Thanks to both of you for the help and insight.

–wpd

I’m using this format to handle a transaction process but it’s not
working. The only difference I can indicate is that I’m testing for
the exception from an object so that I can combine all the errors
together and present them at once when the saves fail:

@success = 1
@request.transaction do
begin
@request.save!
rescue
@request.errors.each do |attr, msg|
@error_messages.push(“Order …” + attr + msg)
end
@success = 0
end

begin
@billing_address.save!
rescue
@billing_address.errors.each do |attr, msg|
@error_messages.push(“BIlling address…” + attr + msg)
end
@success = 0
end

for i in 1…unit_count
begin
@unit.save!
rescue
@unit.errors.each do |attr, msg|
@error_messages.push(“Unit…” + attr +msg)
end
@success = 0
end
end
end

if (@success == 1)
email = Mailer.deliver_confirm(@request,@billing_address)
end
render :layout => false

Would rescuing from the save! prevent the transaction rollback from
happening? Is there a way from within the rescue to call the rollback?

The only other thing to mention is that click on form submit button
calls an AJAX process that does this action in the background and the
resuling XML contains the errors and is parsed to display at the top
of the page. @success is returned in the XML so when no errors occur,
then Javascript redirects to another action. (Why is this done?
Because there is a lot of information on the form that pertains to
different classes and we want all the errors to show up at once rather
than have one returned each time exceptions from a single class save!
are caught.)

On Jun 22, 1:49 am, Brent M. [email protected]