0 is a valid integer value. In the latest version of WTForms, version 3.0.1, IntegerField and InputRequired don't accept 0 as valid. This appears to be an ongoing issue dating back several years. I am proposing a patch, which seems to be working for me.
WTForms 3.0.1 is a great validation library. And I think it is also framework-independent: we can implement our own generic business rules classes, and use it as a basic data validation engine, we can then use these business rules classes in any framework of our own choosing.
I have recently found an issue with it, my form has an IntegerField, and the InputRequired validator, whereby 0 is an acceptable value.
-- But 0 gets rejected!
This issue has been reported over the years, but so far, it is still in this latest version:
- WTForms IntegerField doesn't work properly with NumberRange as validator
- Flask-WTF: How to allow zero upon DataRequired() validation
- InputRequired doesn't accept 0 as valid #100
Following is my attempt to reproduce this issue, and how to get around it. ❶ Create project virtual environment. ❷ Write a test script.
❶ Create project virtual environment. We will need Werkzeug and WTForms 3.0.1 packages.
The test project lives under /webwork/wtforms_test:
$ cd webwork/ $ mkdir wtforms_test $ cd wtforms_test/ behai@HP-Pavilion-15:~/webwork/wtforms_test$ virtualenv venv behai@HP-Pavilion-15:~/webwork/wtforms_test$ source venv/bin/activate (venv) behai@HP-Pavilion-15:~/webwork/wtforms_test$ ./venv/bin/pip install werkzeug WTForms ❷ Test script.
Content of /webwork/wtforms_test$/wtforms_bug.py from pprint import pprint from werkzeug.datastructures import MultiDict from wtforms import ( Form, IntegerField, ) from wtforms.validators import ( InputRequired, NumberRange, ) BREAK_HOUR_01_MSG = "Break Hour must have a value." BREAK_HOUR_02_MSG = "Break Hour is between 0 and 23." class TestForm(Form): break_hour = IntegerField('Break Hour', validators=[InputRequired(BREAK_HOUR_01_MSG), NumberRange(0, 23, BREAK_HOUR_02_MSG)]) def validate(data: dict, form: Form) -> tuple(): form_data = MultiDict(mapping=data) f = form(form_data) res = f.validate() return res, f.errors print("\n--break_hour: -1") res, errors = validate({'break_hour': -1}, TestForm) print(res) pprint(errors) print("\n--break_hour: 'xx'") res, errors = validate({'break_hour': 'xx'}, TestForm) print(res) pprint(errors) print("\n--break_hour: 0") res, errors = validate({'break_hour': 0}, TestForm) print(res) pprint(errors) print("\n--break_hour: 1") res, errors = validate({'break_hour': 1}, TestForm) print(res) pprint(errors) It is simple, the form has only a single integer field break_hour, it is a required field, and accepts any value in the range of 0 to 23 -- and follows by 4 ( four ) tests.
To run:
(venv) behai@HP-Pavilion-15:~/webwork/wtforms_test$ venv/bin/python wtforms_bug.py Output:
--break_hour: -1 False {'break_hour': ['Break Hour is between 0 and 23.']} --break_hour: 'xx' False {'break_hour': ['Not a valid integer value.', 'Break Hour is between 0 and 23.']} --break_hour: 0 False {'break_hour': ['Break Hour must have a value.']} --break_hour: 1 True {} -1 and 'xx' get rejected, which are correct. But 0 gets rejected is a bug: 0 is a valid value.
I traced the issue to InputRequired, I am printing the code for this class below:
InputRequired in ./venv/lib/python3.10/site-packages/wtforms/validators.py class InputRequired: """ Validates that input was provided for this field. Note there is a distinction between this and DataRequired in that InputRequired looks that form-input data was provided, and DataRequired looks at the post-coercion data. Sets the `required` attribute on widgets. """ def __init__(self, message=None): self.message = message self.field_flags = {"required": True} def __call__(self, form, field): if field.raw_data and field.raw_data[0]: return if self.message is None: message = field.gettext("This field is required.") else: message = self.message field.errors[:] = [] raise StopValidation(message) The issue is caused by the first line in def call(self, form, field):
def __call__(self, form, field): if field.raw_data and field.raw_data[0]: return field.raw_data[0] is evaluated to 0, and it is a False, causing the whole if statement to evaluate to False.
Change it to:
if field.raw_data and len(field.raw_data): And it will accept 0 as a value. The correct expected output is now:
--break_hour: -1 False {'break_hour': ['Break Hour is between 0 and 23.']} --break_hour: 'xx' False {'break_hour': ['Not a valid integer value.', 'Break Hour is between 0 and 23.']} --break_hour: 0 True {} --break_hour: 1 True {} Right now, I am just having the change made locally. I am not sure what to do with it just yet. Thank you for reading. I hope this info is useful. Stay safe as always.
✿✿✿
Feature image sources:
- https://in.pinterest.com/pin/337277459600111737/
- https://www.omgubuntu.co.uk/2022/09/ubuntu-2210-kinetic-kudu-default-wallpaper
- https://seeklogo.com/vector-logo/332789/python
- https://github.com/wtforms/wtforms/issues/569, https://user-images.githubusercontent.com/19359364/116413884-4b4e7500-a838-11eb-83b0-704ebb3454b0.png



Top comments (1)
I ended up subclass
InputRequired, and useInputRequiredExin place ofInputRequired: