markmnl
markmnl

Reputation: 11426

How to raise a ValidationError AND return a response?

After checking my form is valid I perform additional validation:

def reset_passwd(req):
    if req.method == 'POST':
        form = ResetPasswdForm(req.POST)
        if form.is_valid():
            # extract data
            passwd1  = form.cleaned_data['passwd1']
            passwd2  = form.cleaned_data['passwd2']
            # validate passwd
            if passwd1 != passwd2:
                raise forms.ValidationError('passwords are not the same', 'passwd_mismatch')
            # do stuff...
            return HttpResponseRedirect('/success')
    else:
        form = ResetPasswdForm(req.POST)
    return render(req, 'reset_passwd_form.html', {'form': form})

Problem is raising a ValidationError which is an Exception of course breaks execution of the view function so no response is returned!

How is one suppose to return their bound form showing validation errors for validation not performed by form.is_valid()?

(The confusing thing is the django documentation say form.is_valid() throws ValidtionErrors if the form is invalid, however it must handel them as debugging it execution continues when is_valid() is false.)

Upvotes: 1

Views: 10450

Answers (2)

FlyingPikachu
FlyingPikachu

Reputation: 135

You need to override the is_valid() method, call the super is_valid() first ( return false if it returns false ), and then handle your error case.

If you use clean() instead you won't benefit for things such as "required=True" in your other fields and will need to check everything manually. super().clean() just ... does not check for it afaik, and it could give you KeyError when accessing cleaned_data['passwd1'] unless checking it yourself.

class MyResetPasswdForm(forms.Form):
    passwd1 = forms.CharField(widget=forms.PasswordInput, required=True)
    passwd2 = forms.CharField(widget=forms.PasswordInput, required=True)

def is_valid(self):
    valid = super(MyResetPasswdForm).is_valid()
    if not valid:
        return valid
    if self.cleaned_data.get("passwd1") != self.cleaned_data.get("passwd2"):
        # You cannot raise a ValidationError because you are not in the clean() method, so you need to add the error through the add_error method.
        self.add_error("passwd2", ValidationError('passwords are not the same', 'passwd_mismatch')
        # You could also use None instead of "passwd2" if you do not want the error message to be linked to the field.
    if not self.errors:
        return True
    return False

This way, you get the pre-set error messages, django handles the fields requirements when you call the super().is_valid().

Upvotes: 0

Rohan
Rohan

Reputation: 53316

To validate such cases, you should use clean() method of a form, rather than raising an error in the view.

This is nicely explained at Cleaning and validating fields that depend on each other

Upvotes: 2

Related Questions