Search code examples
pythonvalidationwtforms

Optional but not empty field in WTForms


The Optional validator allows for both empty values and if the value is not present (from the docs):

class wtforms.validators.Optional(strip_whitespace=True)

Allows empty input and stops the validation chain from continuing.

If input is empty, also removes prior errors (such as processing errors) from the field.

I have some additional validators on a field, and I would like if those validators ran even if the input is an empty string. The builtin Optional validator makes the rest of the validators skipped if the input was an empty string. Is there a built in or any other way to achieve this?

Edit: More specifics about my usecase

I am using this form to validate PUT requests. Let's say I have User entities with usernames as ID and middlenames as an optional field. Then the validator for the fields would look something like:

class UserUpdateForm(Form):
    username = fields.StringField('username', [
        validators.Optional(),
        validators.Length(min=5, max=500)
    ])
    middlename = fields.StringField('middlename', [
        validators.Optional()
    ])

So I would allow for PUT requests that does not have a username or middlename parameter, and those would leave the fields untouched. However, when the parameter is present and is an empty string, I would like the username field validation fail because of the Length validator, but I would allow the middlename field to be set to the empty string.

From another perspective: I would like to distinguish non-present parameters and empty string parameters.


Solution

  • I took a look at the source of the Optional validator:

    class Optional(object):
        ...
        def __call__(self, form, field):
            if not field.raw_data or isinstance(field.raw_data[0], string_types) and not self.string_check(field.raw_data[0]):
                field.errors[:] = []
                raise StopValidation()
    

    As you can see in and not self.string_check(field.raw_data[0]), empty strings are explicitly considered here. I wonder what would happen if I sent two values like a=foo&a=&b=bar.

    Anyway, the quick solution for me was to implement a new validator:

    class OptionalButNotEmpty(object):
        """
        Allows missing but not empty input and stops the validation chain from continuing.
        """
        # Code is a modified version of `Optional` (https://github.com/wtforms/wtforms/blob/master/wtforms/validators.py#L148)
        field_flags = ('optional', )
    
        def __call__(self, form, field):
            if not field.raw_data:
                raise wtforms.validators.StopValidation()