Hello, I have an application where I need to create two records in a database transaction. ie: begin User.transaction(user) do User_details.transaction(user_details) do user.save user_details.save end end end rescue flash[:notice] "Insert rolled back... but why..??" end>From reading the Agile book, I gather that once I wrap the request in atransaction I loose the validation state, so I have no easy what of knowing what went wrong. I am just wonder what the best practise is..?? Is this something people would recommend? user.validate if user.errors.count == 0 user_details.validate if user_details.errors.count == 0 perform transaction_routine(user, user_details) end end rgds, - matt. -- Posted via http://www.ruby-forum.com/.
On Aug 8, 2006, at 8:14 AM, Matt Stone wrote: Not clear on your actual problem, but you could simplify the code as below. As written, I''m not sure that rescue clause was valid at all.> I have an application where I need to create two records in a database > transaction. > > begin > User.transaction(user,user_details) do > user.save! > user_details.save! > end > rescue > flash[:notice] "Insert rolled back... but why..??" > end-- -- Tom Mornini
On Aug 8, 2006, at 8:14 AM, Matt Stone wrote:> I have an application where I need to create two records in a database > transaction. > > begin > User.transaction(user) do > User_details.transaction(user_details) do > user.save > user_details.save > end > end > end > > rescue > flash[:notice] "Insert rolled back... but why..??" > end > > >> From reading the Agile book, I gather that once I wrap the request >> in a > transaction I loose the validation state, so I have no easy what of > knowing what went wrong. > > I am just wonder what the best practise is..?? > > Is this something people would recommend? > > user.validate > if user.errors.count == 0 > user_details.validate > if user_details.errors.count == 0 > perform transaction_routine(user, user_details) > end > end(Transactions are per database connection, not per class or object, so you only need to call it once - the transaction method is available on all of them for convenience.) def update User.transaction do @user.save! @user_details.save! end rescue ActiveRecord::RecordInvalid flash.now[:error] = "error saving #{@user.name}" render :action => ''edit'' end <%= error_messages_for ''user'' %> <%= error_messages_for ''user_details'' %> Though it''s likely more appropriate to manage this in your model, particularly if one object is dependent on the other. jeremy
On 8 Aug 2006, at 16:14, Matt Stone wrote:> Hello, > > I have an application where I need to create two records in a database > transaction. > > ie: > > begin > User.transaction(user) do > User_details.transaction(user_details) do > user.save > user_details.save > end > end > end > > rescue > flash[:notice] "Insert rolled back... but why..??" > end > > >> From reading the Agile book, I gather that once I wrap the request >> in a > transaction I loose the validation state, so I have no easy what of > knowing what went wrong.You only lose the validation if you also have the transaction roll back the active record object as well as the database. If you do this:>> begin > User.transaction(user) doUse.transaction do> User_details.transaction(user_details) do > user.saveYou probably want to do user.save! here The transaction aborts if an exception is thrown, not if the save fails. So you need to use save! to get the RecordInvalid exception thrown out.> user_details.save > end > end > end > > rescueuser has not been rolled back, so contains all the errors from when it was saved> flash[:notice] "Insert rolled back... but why..??" > end> > Is this something people would recommend? > > user.validate > if user.errors.count == 0 > user_details.validate > if user_details.errors.count == 0 > perform transaction_routine(user, user_details) > end > endUse the transaction if you need thread-safety (you don''t need thread safety if you are only serving with a single process, but that doesn''t mean it''s not worth thinking about upfront). The transaction in itself won''t magically make things threadsafe though; you need to think carefully about your failure scenarios and whether all of them cause an exception. For example, your case above creates a user record, then a user_details record. Suppose a separate thread updates the user record before the user_details record has been saved. Is this a problem? Ben -- Ben Blaukopf - Director Airsource Ltd Tel: 01223 708370 / 07786 916043