I''m running Rails 1.2.3, as a precursor. I''ve recently discovered some odd behavior in how Rails treats objects saved in the session and its associations. ************* class Foo < ActiveRecord::Base has_one :bar has_one :barfoo end foo = Foo.new foo.build_bar session[:myfoo] = foo *POST* foo = session[:myfoo] foo.new_record? -> true foo.bar.nil? -> false foo.save foo.build_barfoo foo.barfoo.nil? -> false foo.barfoo.new_record? -> true session[:myfoo] = foo *POST* foo = session[:myfoo] foo.new_record? -> false foo.barfoo.nil? -> true # This is the problem! ************* It looks like what happens is that if you store an object in the session, associations get stripped from the object before serialization IF object.new_record? == false. This is good, most of the time, as it keeps the session size down. However, when the association is unsaved, this is bad. I''m doing a process like so: Object created -> association #1 created -> object and association updated with form data and stored in the session -> preview is rendered -> on confirm, object in session is saved. Later, Object loaded from DB -> association #2 created -> association #2 updated with form data -> object stored in session -> preview rendered -> on confirm, should save the object and its association, but the association is now nil. Is this a bug? Should unsaved associations not be stripped, or am I using the wrong design pattern here? My workaround was to store the association object in the session and to manually reassociate it with the object on each page load, but that''s really kludgy and I''d prefer a more elegant solution if possible. This feels like a bug, though, and it caused me to lose multiple hours tracking it down - it is certainly not intuitive in the slightest. --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk-unsubscribe-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en -~----------~----~----~----~------~----~------~--~---
Antiarc wrote:> Object loaded from DB -> association #2 created -> association #2 > updated with form data -> object stored in session -> preview rendered > -> on confirm, should save the object and its association, but the > association is now nil. > > Is this a bug? Should unsaved associations not be stripped, or am I > using the wrong design pattern here? > > My workaround was to store the association object in the session and > to manually reassociate it with the object on each page load, but > that''s really kludgy and I''d prefer a more elegant solution if > possible. This feels like a bug, though, and it caused me to lose > multiple hours tracking it down - it is certainly not intuitive in the > slightest.Everything stored in session will be serialized, so yes, the design is incorrect. You should save the record id in the session instead of storing the ActiveRecord object in a session O_O. -- Posted via http://www.ruby-forum.com/. --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk-unsubscribe-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en -~----------~----~----~----~------~----~------~--~---
Wai Tsang wrote:> Everything stored in session will be serialized, so yes, the design is > incorrect. You should save the record id in the session instead of > storing the ActiveRecord object in a session O_O.Ok, I may be approaching this incorrectly then. I want to achieve the following: a) Create a new object (Foo) that may be saved to the database at a future point. If the user abandons the process, then nothing should be saved to the database. b) Be able to create association objects (Bars and Barfoos) to be associated with the object, and then possibly saved at some point in the future. If the user abandons the process, the associations should not be saved. My process so far has been: * Create a Foo, set some properties based on form input, and put it in the session. * Add any Bar associations and set their properties via form input * Save the Foo to the session, which serializes its Bar as well. * Preview the Foo with its Bar association * If we decide to commit the changes, Foo (and its Bar) are read in from the session, validated, and saved. * At some point later, a user may load a Foo from the database and want to attach a Barfoo to it. build_barfoo is called, and some properties are set on both the Foo and the Barfoo from form input. * The Foo (and its associated Barfoo) should be saved to the session (this is where it fails) * The user may then preview the results * If the user chooses to commit the changes, the Foo should be saved (UPDATE) and its associated Barfoo should be saved (INSERT). How else could I achieve this workflow? -- Posted via http://www.ruby-forum.com/. --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk-unsubscribe-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en -~----------~----~----~----~------~----~------~--~---
For situations like this, we always save to the database. We''ll use before_filters to load the data from the database on each request.. Every night we run a process to purge incomplete records from the database by checking the created_at date We''re usually wiping out records that are 7 days old or more just to be safe. Using a status flag on the table lets me control what "state" the object''s in, and I can have validations that only get fired on certain states, allowing me to save records at any point in the process. If that idea doesn''t sit well with you, consider the following: 1. If a user''s browser crashes, they lose their work. With the above method, they could come back and finish. 2. Marshalling to the session is prone to problems and can be a performance problem. Just my .02. I''m sure that lots of folks will disagree with my approach. On 4/23/07, Antiarc <rails-mailing-list-ARtvInVfO7ksV2N9l4h3zg@public.gmane.org> wrote:> > > Wai Tsang wrote: > > > Everything stored in session will be serialized, so yes, the design is > > incorrect. You should save the record id in the session instead of > > storing the ActiveRecord object in a session O_O. > > Ok, I may be approaching this incorrectly then. I want to achieve the > following: > > a) Create a new object (Foo) that may be saved to the database at a > future point. If the user abandons the process, then nothing should be > saved to the database. > b) Be able to create association objects (Bars and Barfoos) to be > associated with the object, and then possibly saved at some point in the > future. If the user abandons the process, the associations should not be > saved. > > My process so far has been: > > * Create a Foo, set some properties based on form input, and put it in > the session. > * Add any Bar associations and set their properties via form input > * Save the Foo to the session, which serializes its Bar as well. > * Preview the Foo with its Bar association > * If we decide to commit the changes, Foo (and its Bar) are read in from > the session, validated, and saved. > > * At some point later, a user may load a Foo from the database and want > to attach a Barfoo to it. build_barfoo is called, and some properties > are set on both the Foo and the Barfoo from form input. > * The Foo (and its associated Barfoo) should be saved to the session > (this is where it fails) > * The user may then preview the results > * If the user chooses to commit the changes, the Foo should be saved > (UPDATE) and its associated Barfoo should be saved (INSERT). > > How else could I achieve this workflow? > > -- > Posted via http://www.ruby-forum.com/. > > > >--~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk-unsubscribe-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en -~----------~----~----~----~------~----~------~--~---
I ended up going with this solution (heavily simplified, of course!): -------------------------- class Foo has_one :bar has_one :barfoo end foo = Foo.new params[:foo] foo.build_bar params[:bar] session[:myfoo] = foo *post to preview* -> *confirm save* foo = session[:myfoo] if foo.save then session[:myfoo] = nil end -------------------------- foo = Foo.find params[:id] foo.build_barfoo params[:barfoo] session[:myfoo_id] = foo.id session[:myfoo_barfoo] = foo.barfoo *post to preview* -> *confirm save* foo = Foo.find session[:myfoo_id] foo.barfoo = session[:myfoo_barfoo] if foo.save then session[:myfoo_barfoo] = nil session[:myfoo_id] = nil end -------------------------- This seems to work well. Only unsaved objects are persisted in the session, which gives me pretty good concurrency-proofing, doesn''t force me to deal with the marshaling quirks of an saved object with unsaved associations, and doesn''t leave cruft in the database. It isn''t browser-crash-proof, but this is "cheap data" that doesn''t really need to be protected against crashes. -- Posted via http://www.ruby-forum.com/. --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk-unsubscribe-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en -~----------~----~----~----~------~----~------~--~---