How to create a workflow form

For my project I need many forms of “workflow”. I explain to myself:

The user selects a value in the first field, checks the form and new fields depending on the first value of the field. Then, depending on other fields, new fields may appear ...

How can I implement this in a standard way?

+4
source share
4 answers

I think the solution you are looking for is the django creation wizard

Basically, you define separate forms for different pages and configure the following based on the input on previous screens, at the end you get all the form data together.

In particular, look at the process step advanced option in the forms wizard.

FormWizard.process_step() """ Hook for modifying the wizard internal state, given a fully validated Form object. The Form is guaranteed to have clean, valid data. This method should not modify any of that data. Rather, it might want to set self.extra_context or dynamically alter self.form_list, based on previously submitted forms. Note that this method is called every time a page is rendered for all submitted steps. The function signature: """ def process_step(self, request, form, step): # ... 

If you only need to change the dropdowns based on the other dropdowns in the same form, you should take a look at the implemented dajaxproject

+3
source

I think it depends on the scale of the problem.

You can write some general JavaScript that shows and hides form fields (then in the form itself you apply these css classes). This will work well for a relatively small number displaying and hiding fields.

If you want to go further, you will need to consider developing dynamic forms in Django. I would suggest that you do not modify ['field'] in the class, as Ghislain suggested. There is a good article on dynamic forms here , and this shows you several approaches.

I would suggest that combining the dynamic forms in the post above with django FormWizard could be a good solution. FormWizard guides you through the various forms and then lets you save the general data at the end.

He had several errors, although you cannot easily return to a step without losing step data. Also, to display all forms, you need a little FormWizard customization. Some of the APIs are not documented or considered publicly available (so be careful to change it in future versions of Django), but if you look at the source you can easily expand and redefine parts of the form wizard to do what you need.

Finally, a simpler FormWizard approach would be to say 5 static forms, and then tweak the form selection in the wizard and change which forms follow and show only the corresponding forms. This will work well again, but it depends on how much the shapes change with the previous options.

Hope this helps, ask any questions, if any!

+1
source

It sounds like you need an AJAXy type solution. Checkout the Taconite plugin for jQuery. I use this to pop up dropdowns, etc. On the forms. It works very well.

As for the “general” ... you can have standard methods in your container classes that return lists of children and then have a template fragment t that knows how to format this in a standard way.

-one
source

Well, I found a solution that does not use ajax at all and seems to me quite enjoyable:

Create as many forms as necessary, and subclass them to each other. Put the integer hidden field in the first:

 class Form1(forms.Form): _nextstep = forms.IntegerField(initial = 0, widget = forms.HiddenInput()) foo11 = forms.IntegerField(label = u'First field of the first form') foo12 = forms.IntegerField(label = u'Second field of the first form') class Form2(Form1): foo21 = forms.CharField(label = u'First field of the second form') class Form3(Form2): foo31 = forms.ChoiceField([], label=u'A choice field which choices will be completed\ depending on the previous forms') foo32 = forms.IntegerField(label = u'A last one') # You can alter your fields depending on the data. # Example follows for the foo31 choice field def __init__(self, *args, **kwargs): if self.data and self.data.has_key('foo12'): self.fields['foo31'].choices = ['make','a','nice','list', 'and you can','use your models'] 

Ok, now for the forms now look:

 def myview(request): errors = [] # define the forms used : steps = [Form1,Form2,Form3] if request.method != 'POST': # The first call will use the first form : form = steps[0]() else: step = 0 if request.POST.has_key('_nextstep'): step = int(request.POST['_nextstep']) # Fetch the form class corresponding to this step # and instantiate the form klass = steps[step] form = klass(request.POST) if form.is_valid(): # If the form is valid, increment the step # and use the new class to create the form # that will be displayed data = form.cleaned_data data['_nextstep'] = min(step + 1, len(steps) - 1) klass = steps[data['_nextstep']] form = klass(data) else: errors.append(form.errors) return render_to_response( 'template.html', {'form':form,'errors':errors}, context_instance = RequestContext(request)) 

The only problem that I encountered is that if you use {template}} in your template, it causes form.errors and therefore automatically confirms the new form (for example, Form2) with the data of the previous one (Form1). So what I do is iterate over the elements in the form and use only {{item.id}}, {{item.label}} and {{item}}. Since I already selected the errors of the previous form in the view and passed this to the template, I add a div to display them on top of the page.

-one
source

All Articles