views:

406

answers:

5

Hello, I have a form which spans multiple pages. The way it is set up now is not ideal because it saves (to the database) each page when it is submitted. So if the user does not complete the form across all pages there would be an incomplete user registration saved in the database.

I would like to 'rollback' the saves if the user doesn't fully fill in the form.

So is there a way to set up a transaction that would begin when the user fills out the first form and ends when the user finishes on the last page?

+1  A: 

I don't know the answer to your original question but anyways...

Instead of saving on every page, why not save all the data in a session variable? Then at the end you can have one page that saves the data in session down to the database? This way you never have partially saved anything in the DB.

Jason Punyon
+1  A: 

I think there's value in saving each page, because if it's a long-running process and the user is interrupted in the middle they may want to save their intermediate, uncommitted results and pick up where they left off later.

If you think that's a good idea too, then your design would store a flag to indicate whether a particular piece was part of a long-running transaction that wasn't complete yet. Set the flag when the final piece was done and the save button was hit. Give a timeout value that would say if the intermediate pieces were given "final save" approval after a certain period of time they'd be deleted.

duffymo
+5  A: 

What you're looking for is the acts_as_state_machine gem. If you're unfamiliar with State Machines, take a look here.

Matt Darby
+1  A: 

Opening a database transaction that would span multiple requests is a bad idea. Consider the situation where a user just closes the browser before your transaction is commited. You are left with an orphaned transaction that is neither rolledback nor commited.

Just put a flag in the database something like REG_COMPLETE, that only gets set on the last page of the registration process. Then you can filter/scrub/whatever you like with that incomplete record...maybe send them an email prompting them to finish?

Al W
+2  A: 

To answer the specific question, I don't think there is any way to set up a transaction in the database sense that will do what you want. Think about it and you'll see why: there's no guarantee that the various parts of your multi-page operation will be handled by the same process. Or quite likely even the same server. If the request spans database connections, as it will in this situation, the uncommitted parts from one connection will be invisible to the other connections.

As well as the ideas already mentioned, I'd consider using one or more "staging" tables to hold the incomplete data already entered. Then when the user is finished, a single transaction can apply the data to the permanent tables and remove the staging data. Incomplete data can be cleared out on an age criterion by a background process once you're confident the session has been terminated, or held until the user returns, should that better meet your requirements.

I'd lean towards this approach particularly if I expected to have regular incomplete transactions, because this way I don't have to deal with incomplete data in my main models.

Mike Woodhouse