Search code examples
djangoformsvalidationdjango-formwizard

Django form wizard validate (clean) form using previous form


Question in short:

I am using a django Form Wizard to show several forms after each other. Is there a clean way to validate a later form (at step Y) using data of the previous forms (of steps X < Y)?

Details:

Standard django form validation is done in the form's clean() method at each step and the wizard only proceeds to the next step, if the current form is valid and does not raise a ValidationError. I now have a situation, where the validation of the form of a later step (X) depends on the data of an earlier form (Y < X). Only if these two forms have compatible data, I want the wizard to accept form Y and proceed to step Y+1.

The django documentation specifies how form and field validation works for the following instances:

  • a single field,

    The clean_< fieldname >() method is called on a form subclass – where is replaced with the name of the form field attribute. This method does any cleaning that is specific to that particular attribute, unrelated to the type of field that it is.

  • several fields of the same form

    The form subclass’s clean() method can perform validation that requires access to multiple form fields.

However, I cannot find any place where I can validate the current step using all data of previous steps. I would have expected a clean() method in the form wizard. However, I cannot find it.


Solution

  • It can be achieved as follows:

    1. Overwrite the get_form_initial(self, step) method of the wizard. Handle the later step as follows:
      1. retrieve the cleaned data of the earlier step;
      2. retrieve the initial data for this step (super call);
      3. merge the two dictionaries and return the result;
    2. In the clean method of the later form access the data of the earlier form via the self.initial field.

    Code snippet for the Wizard class:

    def get_form_initial(self, step):
        if step == '5':
            step4 = self.get_cleaned_data_for_step('3')
            res = super(DenovoPatternWizard, self).get_form_initial(step)
            res['extendsequencedb'] = {}
            res['extendsequencedb']['include_most_similar_pattern_sequences'] = step4['sequencepatternassignment'][
                'number_sites_per_pattern']
            return res
    

    Code snippet of the later form:

    def clean(self):
        cleaned_data = super(InputDenovoPatternPatternForm, self).clean()
        # compatibility of earlier and current step
    
        if self.initial['timeseries_data'].number_timepoints != cleaned_data['pattern_data'].number_timepoints:
            raise ValidationError("The time-series and pattern input files need to have the same number of timepoints (%d and %d respectively)." %
                                  (self.initial['timeseries_data'].number_timepoints, cleaned_data['pattern_data'].number_timepoints))