'Best Practice: Processing complex dynamic form on single-page with Django

I'm looking for the most pythonic way to handle a complex form on a single-page. My "submit" page has a series of questions that, based on the user's input, will generate different forms/fields.

A simplified example:

                                                               --- Shift 1 --> Form #1 
                                    --- Yes -- What shift? -- |
                                    |                          --- Shift 2 --> Form #2
                                    | 
Enter Date ---   Did you work?   ---|
                                    |
                                    |                            --- Yes --> Form #3
                                     --- No -- Was this PTO? ---|
                                                                 --- No --> Form #4

I'm trying to figure out most efficient/pythonic way to handle the above.

Possible Approaches:

  1. Lots of jquery, ajax, and function based views. This is my current setup, but I'd like to move it to CBV and ModelForms if possible because my current code is about 2000 lines (with 10+ ajax functions, 10+ view-handling urls, and too many input fields to count) within my template/models/views just to load/process this form. It is a complete nightmare to maintain.
  2. Single page with ModelForms embedded within a dynamic div. The form page calls a jquery load depending on your answers to the questions. For instance, if you answered "Today", "Yes", "Shift 1", I would call $("#form-embed").load("/forms/form1/") which contains the Form #1 ModelForm. A couple problems with this: 1. Some of the "questions" are actually form fields, and if I'm reloading a blank form, then that field would be blank. I could solve this with JS on the front-end or custom cleaning on the back-end, but this ends up being a lot of code as well. And 2. Form POSTing and error handling would be pretty complex with so many potential forms on one page.
  3. Same as above but with an Iframe. This solves some of the above issues, but feels "icky" for some reason and makes me worry about responsive UI/compatibility across platforms.
  4. Multiple pages, each with its own form. This eliminates the convenience of a single-page approach, which I'd really like to keep if possible.
  5. Combine all my models into a single model. This gets pretty complex pretty quickly because the actual fields involved on each form are very different, and the logic handling would therefore get messy and bloated.
  6. Create a new single model with a corresponding modelform for use only with this form that contains all possible fields, dynamically displayed via JS based on user input, then routes the fields to the appropriate models after submission.
  7. Don't use modelforms, instead create my own custom Form Class that involves all possible fields, then route to the appropriate models after submission. Use JS to display/hide fields based on user input.

Is there an approach I'm missing, or is one of these the best? These would all theoretically work just fine, but none of them feels very pythonic. #7 maybe feels like the best option, but I'm still not super happy with it.

My hope for the front-end is to maximize ModelForm and Django's template management, while minimizing JS. My hope for the back-end is to maximize CBV/ModelForm customization while minimizing FBV. My overall goal is to reduce code volume and improve maintainability/modification.



Solution 1:[1]

Looks like the functionality I was looking for is the class-based view FormView and forms.Form (not forms.ModelFormView).

In forms.py, I created a forms.Form form which contained all possible fields (ended up being about 10, able to reuse several. Had to make a lot of them required=False), then loaded them all at the same time in individual divs within a FormView. Each div shared the name of the field, so I could easily hide/show each div using simple JS depending on what combination of fields I wanted to display based on user input.

I also included a hidden "form number" field which updates based on the final input, which helps to direct my form validation.

I then used the def clean(self) mixin in my forms.py to process each individual form.

Haven't finished the entire thing yet, but it's already looking like my code length will be cut from 2000+ down to a couple hundred or so. Plus this will be much easier to maintain, and keeps most of my logic on the serverside rather than in the html file.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Josh