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 Doyle wrote:

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

Brent Miller wrote:

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

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 Doyle wrote:

Fabulous! Thank you very much.

--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.

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.

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

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