Am I setting myself up for disaster by storing an
ActiveRecord model in the session before saving it to the database? I’ve
heard that they aren’t serializable and that this is a bad idea…
If so, is there any way to serialize the record for session storage? This
technique just seems so useful for me… (ie multiple step checkout, etc.)
IMHO this is a good way to prevent session issues. My
understanding is that you’ll need to do some clever validataions for
multi step forms and such. Otherwise you could have a situation
where you have not got valid models and thus they won’t save.
Thanks Brian for the suggestion... I might end up doing that but I'd
prefer not to write to the database prematurely (ie before the final
review and submit button in checkout has been pressed). Sure I can
store the status and just set the status to incomplete until the
process is complete, but this is simply not ideal in many cases. That
would seem to leave a lot of unnecessary database integrity issues.
you're better off storing the object id in the session object.
session[:object] = myobject.id
then you can reload the object by
myobject = MyObject.find(session[:object])
then each action can update the object in turn
On an existing object yes. But the original poster said they wanted to store a new unsaved object in the session. It does not seem like there would be any problem with this since there is no issue of contention. But I'm not extremely knowledgeable about ActiveRecord so perhaps there is some valid reason for saying no in this case.
In any case it seems like you could just store a hash of attributes in the session as you go through the multi-page form and then at the end do:
MyObject.create session[:my_obj]
Where session[:my_obj] is just a hash of attributes that are assigned as the form progresses. But I still think using an unsaved ActiveRecord object would be just as safe as a Hash.
What about using a Struct or OpenStruct that mimics your model, and
sticking that into the session? You could also assign values to your
model, validate them, and then if the validation succeeds don't save
the model but do save the struct. Then when you are finally ready to
save the model you could do something like so:
I like the idea of storing a hash of attributes. The serialization is
in your control.
Now there's nothing wrong with creating a new object and saving it
before all the data is ready. For example, you have objects that have
a parent-child relationship. You create the parent first. Then you
create the children in a loop (parent.children.create). While creating
the children, you process them to get some intrinsic value like count
(a simplistic example already provided by acts_as_tree). Then
post-loop you update the parent with the calculated value.
Of course, you could simply use the new method instead of create and
save the parent at the end, which will save the parent and children at
the same time. However if your object tree is being created
recursively, and the tree is made up of a lot of objects, and the
tables that store the objects are wide, well you can run out of memory
quickly.
In any case, a solution is highly dependent on the circumstances and
problem to be solved. There's no one size fits all.
But when it comes to storing data in a session variable, the less
complex the better. As Frederick says, if you change your model and
forget to update how you store model data in the session, something
will go wrong.
Also you really should avoid storing critical data in a session. If a
cookie is lost or deleted, the application can lose track of the
session. Storing it in a database object like a Struct can be handy in
that case.
Then of course there's the old stand-by of playing hot potato, and
passing the data from one form and populating hidden fields of the next
form.
Or you can be really hip and AJAXy and use script.aculo.us to hide and
unhide portions of a megalithic form which will make your server-side
code much simpler (1 method rather than a series of methods, 1 form
instead of many) yet to the user it'll be a wizard-like interface that
is VERY responsive. Plus it'll be easier to debug and update since it
is only one form, and you can always switch off the form_remote_tag to
regular form_tag while developing (which is recommended... get your
non-AJAX stuff working first).
oh I should clarify...form_remote_tag (XmlHttpResponse) and
script.aculo.us are separate features. You don't need one to make use
of the other. It's just they are used together very often. Your form
can have all these nifty hide/unhide events but depending on the scope
of the data being entered, it should post like a regular form if that's
the desired behavior.
Awesome thanks guys... that is definitely sufficient information to go
off of. I'll probably validate the model object but store a hash or
[open]struct in the session.