2015-11-22

Django Form - Confirm Before Saving (Part 2)

In Part 1 of this post, we create a form which conditionally shows a checkbox for user confirmation. The implementation is to raise an validation error when a condition is met. That does not work well with multiple confirmations, as the checking stops when the first condition is met. We need a way to check all conditions without raising an validation error, with one checkbox to confirm them all. We can do that with Form.add_error()
Instead of raising an validation error like in Part 1 of this post:
class InvoiceForm(forms.ModelForm):
    ...
    def clean(self):
        super().clean()
        ...
       if all((key in self.cleaned_data for key in ['date', 'confirm'])):
           if self.cleaned_data['date'].weekday() == 6 and not self.cleaned_data['confirm']:
                raise forms.ValidationError(MSG001)
We add an validation error when a condition is met:
class InvoiceForm(forms.ModelForm):
    ...
    def clean(self):
        super().clean()
        ...
        if 'confirm' in self.cleaned_data and not self.cleaned_data['confirm']:
            if 'date' in self.cleaned_data:
                d = self.cleaned_data['date']
                if d.weekday() == 6:
                    self.add_error(None, forms.ValidationError(CONFIRM_MSG[1]))
                if (datetime.date.today() - d).days > 14:
                    self.add_error(None, forms.ValidationError(CONFIRM_MSG[2]))
In the example above, a confirmation is required if either the invoice date falls on a Sunday or the invoice date is more than 14 days in the past. CONFIRM_MSG is a dict of confirmation messages such as the following:
CONFIRM_MSG = {
    1: "The invoice date does not fall on a Sunday.  Please check confirm to proceed.",
    2: "The invoice date is more than 14 days in the past.  Please check confirm to proceed.",
}
In the Form.__init__() method, we check for existence of any of the confirmation messages to show the checkbox. Note the use of the Form.non_field_errors() method to retrieve all non-field errors, such as those added in the Form.clean().
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        ...
        confirm_set = set(CONFIRM_MSG.values())
        if confirm_set.intersection(self.non_field_errors()):
            self.fields['confirm'].widget = forms.CheckboxInput()

No comments: