django-formtools
django-formtools copied to clipboard
FormWizard validates the last form twice
This issue was originally submitted on Trac (#10810). See the previous link for the full conversation. Below is the original description:
FormWizard revalidates all the forms on the last step, after validating the last form. So the last one gets validated twice on the same request. In my app I have captcha on the last form and it does not work with revalidation (and should not I think). I've made changes to wizard.py, patch attached (see original Trac issue).
An interesting comment from the Trac discussion:
Changing the re-validation and making the last step a special case isn't a good idea. Think about a case when someone want's to add a captcha in the first step.. this would break too.
What do you think about a list of "don't re-validate" forms/steps? This would give people a way to mark specific steps as "validating once is fine, skip this form when re-validating everything for the done method".
I think this is a good point. The case given here is that the validation is failing because this user's case had a captcha in the last form. However, should the captcha have been in any of the other forms, it would have failed too. I like the suggestion of allowing certain forms to skip the final validation better than just skipping the re-validation of the last form. Interested to hear what others think, though.
We now have the same issue but we need to put the captcha on the first step.
As a workaround we will try to dynamically delete captcha field in get_form method if the form was already valid (there are data in the storage). If this works then generic solution can be made where you configure in which form which fields must be validated only once and then the wizard will automatically delete them when needed.
here's a mixin. add it to the view
class PreventRevalidationMixin:
NO_REVALIDATION_FIELD_NAMES = ['captcha']
def process_step(self, form):
for name in self.NO_REVALIDATION_FIELD_NAMES:
if name in form.fields:
self.storage.extra_data[f'skip_validation{name}'] = True
return super().process_step(form)
def get_form(self, step=None, *args, **kwargs):
form = super().get_form(step=step, *args, **kwargs)
for name in self.NO_REVALIDATION_FIELD_NAMES:
if name in form.fields and self.storage.extra_data.get(f'skip_validation{name}')
del form.fields[name]
return form