[Preamble: Whereas I realize there may be simpler ways to do this (i.e., just use Django built-in Admin, or use inlines to do all editing on one page, etc.), unfortunately, I am not in control of the designs, so I'm seeking help on how to work with what I've been tasked with implementing.]
I have 2 models, Deck and Slide. Slide has foreign key to Deck. (there is also an intermediate model--its a m2m relationship--but to simplify an already complex discussion, I'm going to pretend its a simpler many-to-one relationship.)
The interface i am implementing displays a page with a form to enter or edit Deck fields which also includes an embedded list of Slides in the Deck, with some fields (which I'm in the process of making text inputs) and also with an "edit" and a "remove" anchor link for each Slide. (see img) If you click an "edit" link, it takes you to a new page with a detailed form to input all the information representing the content for the corresponding slide. If you click submit in that form, it takes you back to the page for the deck.
As the title question proposes, I obviously don't want to commit any Deck or Slides to the DB until a user clicks Submit for the entire Deck, i.e. they can add or edit many slides in the interim and may decide to cancel the whole process.
What is the best, cleanest way of doing this?
I've looked at Django's FormWizard class ( http://docs.djangoproject.com/en/dev/ref/contrib/formtools/form-wizard/ ), but seems to be geared towards a multi-step linear flow, not my situation.
I already had to implement this for a demo, and I've gotten most of the way there by creating an inline FormSet for the Slides and a Form for the Deck, and then writing subclasses for my Forms that hide the form and the formset as I pass back and forth between the two pages. Here's some code demonstrating how I use a hidden form for the deck in the slide page:
class DeckForm(ModelForm):
class Meta:
#stuff here
class HiddenDeckForm(DeckForm):
def __init__(self, *args, **kwargs):
super(DeckHiddenForm, self).__init__(*args, **kwargs)
for name, field in self.fields.iteritems():
field.widget = field.hidden_widget()
field.required = False
So in my views, every time I go to the slide form page, I generate a HiddenDeckForm from the POST data and pass it in, and then in my view going back to the deck page, I regenerate the DeckForm (not hidden subclass) from the POST data. Not going to post all my Slide form code, since I'm basically asking whether there is a better way of doing it, but similarly, I have a HiddenSlideForm class, and pass a formset of those between my templates to save the state.
Then when the user clicks submit on the Deck page, the Deck form and Slides formset are all saved to the DB.
It works, but is this a good way to do it? Its been a good bit of code, and I've really had to dig into some Django internals--feel's like I'm using things in a way they weren't designed to be used. Or is there already a more standard way of handling this scenario?
I'll post more code if its helpful.
Thanks for the help!
PS As you can see, I'm implementing it in a jquery colorbox and using ajax for the transitions between the forms, but I am just using normal template/form rendering in my views and passing the rendered html back to the page with an ajax call . . . guess I could do something with json, but strongly prefer to stick with passing a rendered template, as such an implementation could be used without ajax or javascript if desired.
Idea A: Store the uncommitted data in the session until everything is ready for commit. Each form/roundtrip to the client just adds/updates the accumulating data. If they abandon it you only have crap lying around until the session is destroyed. You can save pretty much anything to the session.
Idea B: Add a boolean to the database table which indicates when a row has been committed (eg: "is_active" or "is_pending"). Means you never lose anything but a bit more of a nuisance to manage.
Having done the very similar requirement by working around the formset tools, I completely agree that going hoops and bounds to understand/customize the formset is totally not worth it.
For a screenshot like yours, I'd use just one form with all of the slides per deck. - Form, not formset.
You should handle the slide "edit/delete/new" all in ajax requests that include the deck you create when someone creates a "new deck". And then in the "Deck Form", you only change the
Deck
properties like name and association.Or, if you are inlined to do all new elements in the page itself without Ajax and creating new "Slide" objects, you can use the
formset
and save the deck and all associated slides.