2

I have a working django formwizard which when I hit the previous button doesn't validate the current input.

I've tried variations on

<input name="wizard_goto_step" class="btn btn-primary btn-large" type="submit" value="prev"/>

and

<button class="btn btn-info btn-large"
        name="wizard_goto_step" type="submit" value="{{ wizard.steps.prev }}">
    {% trans "prev step" %}
</button>

but neither of these seems to do what I want to do.

gmorell
  • 53
  • 1
  • 7
  • 1
    I think that's expected as user would like to submit data only when going to next step. When moving to previous step user might not have filled the any data in current step! – Rohan Sep 27 '13 at 04:57
  • Why would it validate data when going to the previous step? You will be posting to a different view (the previous one) in that case so of course it won't validate. – Timmy O'Mahony Sep 27 '13 at 08:05
  • I've had users complain about it not saving data when going to the previous step. :\ – gmorell Sep 28 '13 at 02:22

3 Answers3

2

If you want it to validate and save the data on the current form before stepping back to a previous form, you need to override the post() method in your subclass of SessionWizardView. The methods you're looking for are self.storage.set_step_data() and self.storage.set_step_files() to save the current form data.

A rough example:

def post(self, *args, **kwargs):                                                                                                                                                                                                                    
    go_to_step = self.request.POST.get('wizard_goto_step', None)  # get the step name                                                                                                                                               
    form = self.get_form(data=self.request.POST, files=self.request.FILES)                                                      

    current_index = self.get_step_index(self.steps.current)                                                                     
    goto_index = self.get_step_index(go_to_step)                                                                                

    if current_index > goto_index:    
        if form.is_valid():                                                                      
            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))                                                                                      
    else:                                                                                                                       
        return self.render(form)                                                                                                
    return super(NominateFormWizard, self).post(*args, **kwargs) 
3cheesewheel
  • 9,133
  • 9
  • 39
  • 59
  • 1
    When I was trying goto the next form it simply redisplayed current form so I had to add `if not go_to_step == None` condition – HenryM Jul 24 '19 at 08:05
2

One (arguably elegant) way would be to use a different POST variable than wizard_goto_step, and then override WizardView.get_next_step():

def get_next_step(self, step=None):
    return self.request.POST.get('wizard_next_step',
            super().get_next_step(step))

Then, use name="wizard_next_step" on the previous-step button/link. This approach both has the advantage that the old behavior remains available should you need it, and that you're not re-implementing WizardView.post().

Brian H
  • 146
  • 3
  • Good solution but doesn't this imply that if you are on the final step and you want to go backwards, so you click on one of the buttons, but it's actually going to submit the form and run the `done` function... Might need some extra logic to handle that. – jhrr Jul 10 '17 at 16:13
1

This question is a bit old, but I had this issue and wanted to provide a solution in case someone else is googling this question.

I liked Brian H's solution, but if you are on the final step and you want to go to the previous step, it's actually going to submit the form and run the done function. I could not find a solution to fix this, so I will propose a different solution. All you need to do is overwrite the render_goto_step on the WizardView.

def render_goto_step(self, *args, **kwargs):
    form = self.get_form(data=self.request.POST, files=self.request.FILES)
    if form.is_valid():
        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))
    return super().render_goto_step(*args, **kwargs)

Essentially, it saves the data of the current form before it renders the given step, but only if the form is valid. I suppose you could save the data regardless if it's valid or not, but I haven't tested it this way.

bad_coder
  • 11,289
  • 20
  • 44
  • 72
Enrique
  • 11
  • 1
  • Thanks! This solved the issue for me and seems that the wizard works flawlessly with this. – Filip Oct 25 '22 at 18:35