Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Optional doesn't support multivalued fields #835

Open
markhobson opened this issue Feb 8, 2024 · 2 comments
Open

Optional doesn't support multivalued fields #835

markhobson opened this issue Feb 8, 2024 · 2 comments

Comments

@markhobson
Copy link

The Optional validator is used to short-circuit validation to allow empty field values. This works for single-valued fields but not for multivalued fields. For example, when DateField is used with separate day, month and year controls.

Actual Behavior

>>> import wtforms
>>> from werkzeug.datastructures import MultiDict
>>> class F(wtforms.Form):
...     foo = wtforms.DateField(format="%d %m %Y", validators=[wtforms.validators.Optional()])
>>> f = F(formdata=MultiDict([("foo", ""), ("foo", "3"), ("foo", "2007")]))
>>> print(f.validate())
True

Expected Behavior

>>> import wtforms
>>> from werkzeug.datastructures import MultiDict
>>> class F(wtforms.Form):
...     foo = wtforms.DateField(format="%d %m %Y", validators=[wtforms.validators.Optional()])
>>> f = F(formdata=MultiDict([("foo", ""), ("foo", "3"), ("foo", "2007")]))
>>> print(f.validate())
False
>>> print(f.foo.errors)
['Not a valid date value.']

This issue is due to Optional only considering the first raw data value.

A custom validator can be used to work around this issue:

class MultivalueOptional(Optional):
    """
    A validator that allows empty input and supports multivalued fields.
    """

    def __call__(self, form: BaseForm, field: Field) -> None:
        if not field.raw_data or all(self._is_empty(value) for value in field.raw_data):
            field.errors = []
            raise StopValidation()

    def _is_empty(self, value: Any) -> bool:
        return isinstance(value, str) and not self.string_check(value)

Although this functionality could easily be accommodated by the library's Optional validator.

Environment

  • Python version: 3.12.1
  • wtforms version: 3.1.2
@markhobson
Copy link
Author

A similar issue exists for InputRequired. Here's a workaround for that:

class MultivalueInputRequired(InputRequired):
    """
    A validator that ensures input was provided and supports multivalued fields.
    """

    def __call__(self, form: BaseForm, field: Field) -> None:
        if field.raw_data and all(field.raw_data):
            return

        if self.message is None:
            message = field.gettext("This field is required.")
        else:
            message = self.message

        field.errors = []
        raise StopValidation(message)

@Daverball
Copy link

This is a general design flaw with WTForms. There's many other ways to break Optional/InputRequired/DataRequired.

The best way to clean this up, would be to change the API in 4.0 and expect individual Field classes to deal with the implementation details of what it means for that field to be optional/required.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

No branches or pull requests

2 participants