pallets-eco / wtforms

A flexible forms validation and rendering library for Python.
https://wtforms.readthedocs.io
BSD 3-Clause "New" or "Revised" License
1.51k stars 395 forks source link

Validator order isn't immediately apparent #852

Open miketheman opened 1 month ago

miketheman commented 1 month ago

I was trying to understand why validators placed on the field wouldn't be validated prior to in-line validators, and came up with this (albeit impossible) scenario to demonstrate the problem.

Actual Behavior

In this example, the StringField validator isn't being called at all, as shown in f.errors:

>>> from webob.multidict import MultiDict
>>> from wtforms import Form, StringField
>>> from wtforms.validators import ValidationError, Length
>>>
>>> class F(Form):
...     name = StringField(validators=[Length(min=50)])
...     def validate_name(form, field):
...         if len(field.data) > 50:
...             raise ValidationError("Name must be less than 50 characters")
...
>>> f = F(MultiDict([("name", "a" * 51)]))
>>>
>>> assert not f.validate()
>>> assert f.errors == {"name": ["Name must be less than 50 characters"]}

Expected Behavior

In this example, I flip the input condition to a shorter string, and expect that the Length validator tell me that it's not long enough before calling validate_name().

>>> from webob.multidict import MultiDict
>>> from wtforms import Form, StringField
>>> from wtforms.validators import ValidationError, Length
>>>
>>> class F(Form):
...     name = StringField(validators=[Length(min=50)])
...     def validate_name(form, field):
...         if len(field.data) > 50:
...             raise ValidationError("Name must be less than 50 characters")
...
>>> f = F(MultiDict([("name", "a")]))
>>>
>>> assert not f.validate()
>>> assert f.errors == {"name": ["Field must be at least 50 characters long."]}

In my head, the field validation should happen before a form's in-line field validation, but had trouble finding the order expressed in the documentation.

Environment