Search code examples
djangoformsdynamicdjango-formwizard

Django Formwizard with dynamic form does not proceed to next step


I am trying to create a django formwizard where the first form has a choicefield generated at runtime. I can generate the form with the correct choices but when I click submit the formwizard does not proceed to the next step. It submits the form but returns to form1. Here are my forms and view

##Forms
class EventForm1(forms.Form):
    def __init__(self, *args, **kwargs):
        ##Get choices at runtime
        choices = kwargs.pop('loan_choices', [])
        super(EventForm1, self).__init__(*args, **kwargs)
        self.fields['loan'] = forms.ChoiceField(choices=choices, required=False, widget=forms.Select(attrs={'class':'events_loan_name', 'event_attribute':'loan', 'name':'loan'}) )
    attribute_to_change = forms.ChoiceField(choices=[('monthly_payment','Monthly Payment'), ('interest_rate','Interest Rate')], widget=forms.Select(attrs={'class': 'attribute_to_change', 'name':'attribute_to_change', 'event_attribute':'attribute_to_change'}))

class EventForm2(forms.Form):
    start_date = forms.DateField(widget=forms.TextInput(attrs={'class': 'event_dates','start_date':'start_date', 'name':'event_start_date', 'event_attribute':'start_date'}))

##Views
class EventFormWizard(SessionWizardView):
    """Form wizard for adding an event"""

    def done(self, form_list, **kwargs):
        self.finalize_new_event(form_list)
        return HttpResponseRedirect('/')

    def get_form(self, step=None, data=None, files=None):
        form = super(EventFormWizard, self).get_form(step, data, files)

        # determine the step if not given
        if step is None:
            step = self.steps.current

        print(step, form)
        if step == "0":
            loan_sessions = self.request.session.get('loan_sessions', None)
            if loan_sessions != None:
                loan_sessions = pickle.loads(self.request.session['loan_sessions'])
            else:
                loan_sessions = []

            loan_choices = []
            for loan_session in loan_sessions:
                loan_choice = (loan_session['number'], loan_session['name'])
                loan_choices.append(loan_choice)

            ##Update the choices for the select 
            form = EventForm1(loan_choices=loan_choices)

        return form

    def finalize_new_loan(self, form_list):
        """Adds a autmatic properties to the new loan, saves it to the loan_sessions and deletes the new loan object from the session"""

        for form in form_list:
            cleaned_data = form.cleaned_data
            print(cleaned_data)

I'm using django 1.8 with the form-tools package.


Solution

  • I discovered what's going on. The form wizard's post method calls get_form like so:

            # get the form for the current step
            form = self.get_form(data=self.request.POST, files=self.request.FILES)
    
            print(form.is_valid(), form.errors, form.is_bound)
            # and try to validate
            if form.is_valid():
                # if the form is valid, store the cleaned data and files.
                self.storage.set_step_data(self.steps.current,
                                           self.process_step(form))
                self.storage.set_step_files(self.steps.current,
                                            self.process_step_files(form))
    
                # check if the current step is the last step
                if self.steps.current == self.steps.last:
                    # no more steps, render done view
                    return self.render_done(form, **kwargs)
                else:
                    # proceed to the next step
                    return self.render_next_step(form)
            return self.render(form)
    

    It only calls the next step's form if the current form validates, which it never does because my custom get_form hook doesn't check for the POST data when creating the dynamic form. Anyway here is the working custom get_form hook:

        def get_form(self, step=None, data=None, files=None):
            form = super(EventFormWizard, self).get_form(step, data, files)
    
            # determine the step if not given
            if step is None:
                step = self.steps.current
    
            if step == "0":
                loan_sessions = self.request.session.get('loan_sessions', None)
                if loan_sessions != None:
                    loan_sessions = pickle.loads(self.request.session['loan_sessions'])
                else:
                    loan_sessions = []
    
                loan_choices = []
                for loan_session in loan_sessions:
                    loan_choice = (loan_session['number'], loan_session['name'])
                    loan_choices.append(loan_choice)
    
                ##Pass the data when initing the form, which is the POST
                ## data if the got_form function called during a post
                ## or the self.storage.get_step_data(form_key) if the form wizard
                ## is validating this form again in the render_done method
                form = EventForm1(loan_choices=loan_choices, data=data)
    
            return form