Can wtforms custom validator make a field optional?

MKaras picture MKaras · Dec 9, 2011 · Viewed 7.1k times · Source

I'm using a custom validator to check a field is not empty if a check box is checked. It checks correctly but regardless it always still validating if the value is a number.

Basically I need a field to stop validation under certain conditions of the form.

Is there a way for the custom validator to stop validation on the field?

Answer

Crast picture Crast · Dec 9, 2011

Yes, custom validators can control the validation flow just like the built-in Optional and Required validators. To control the validation flow, you use the StopValidation exception, and no further validation will be done.

If StopValidation is raised with a message, it will be added to the errors list, otherwise if there is no message, no more errors will be added.

If you are using, say, IntegerField, FloatField, etc.. you also have to keep in mind the "processing errors" which occur at input coercion time. The way the Optional validator handles this is it clears all previous errors if the input is empty. Let's just take a quick look at the code for the Optional validator from wtforms/fields.py:

if not field.raw_data or isinstance(field.raw_data[0], basestring) and not field.raw_data[0].strip():
    field.errors[:] = []
    raise StopValidation()

As you can see one of the things it does if there is no input or blank input, is it will clear out any previous errors.

So, let's come up with how you could do your custom validator.

from wtforms.validators import StopValidation

def myvalidator(form, field):
    if not form.some_checkbox_field.data:
        # clear out processing errors
        field.errors[:] = []
        # Stop further validators running
        raise StopValidation()

You could then use your validator like such:

from wtforms import BooleanField, IntegerField, Form, validators as v

class SomeForm(Form):
    some_checkbox_field = BooleanField('Enable MyNumber')
    mynumber = IntegerField('', [myvalidator, v.NumberRange(min=5, max=50)])

So then, if the checkbox is checked, it will validate that mynumber was a number as inputted. In addition, the NumberRange validator will be run. If not checked, errors will be cleared, and the StopValidation will prevent the NumberRange from running.