WTForms: IntegerField skips coercion on a string v

2020-04-21 00:18发布

I have a Form instance with a single IntegerField.

The IntegerField renders to HTML as an <input> with type="text" and data gets POSTed back from an HTML form as a text string. However the form will not validate if the posted data has a string value for the IntegerField (passed in via a dict in the data parameter).

Here's a toy example:

from wtforms import validators, Form, IntegerField 

class TestForm(Form):
    num = IntegerField('How Many?', [validators.NumberRange(min=1, max=100)])


test_form1 = TestForm()
print("HTML Render 1: %s" % test_form1.num())

data_in = {'num': '66'}  # Note '66' is a string as would be POSTed
test_form2 = TestForm(data=data_in)
print("HTML Render 2: %s" % test_form2.num())
print("     Validate: %s" % test_form2.validate())
print("       Errors: %s" % test_form2.errors)

The output is:

HTML Render 1: <input id="num" name="num" type="text" value="">
HTML Render 2: <input id="num" name="num" type="text" value="66">
     Validate: False
       Errors: {'num': [u'Number must be between 1 and 100.']}

The docstring for IntegerField says:

IntegerField(Field): A text field, except all input is coerced to an integer

How can I coerce a str into an int such that this form will pass validation?

1条回答
萌系小妹纸
2楼-- · 2020-04-21 00:46

This is from one of the WTForms devs:

Fields only coerce form data, they don't coerce object data, this lets people use objects >"like an int" and still have them work without the value being clobbered. It's your >responsibility to pass correct datatypes to object/kwargs data.

And from the docs:

process_formdata(valuelist) Process data received over the wire from a form.

This will be called during form construction with data supplied through the formdata argument.

Parameter: valuelist – A list of strings to process.

In your example the process_formdata method on IntegerField will never be called

You are passing in a str and this will not be coerced because you are supplying it as the data keyword argument. The data keyword argument signifies exactly the data you want to validate without coercion. Because '66' is still a str the validators won't let it pass.

The formdata keyword argument indicates the data coming in off the wire. This will go through the field's coercion process. There is only one catch, it only accepts MultiDict like objects. If you look at the example below I've used the webob MutliDict but there is also one supplied in the Werkzeug library. If you wrap a regular python dictionary in a MultiDict and supply it as the formdata keyword your form will validate as expected.

from wtforms import validators, Form, IntegerField 
from webob.multidict import MultiDict

class TestForm(Form):
    num = IntegerField('How Many?', [validators.NumberRange(min=1, max=100)])

data_in = {'num': '66'}  # Note '66' is a string as would be POSTed
test_form2 = TestForm(formdata=MultiDict(data_in))
print("HTML Render 2: %s" % test_form2.num())
print("     Validate: %s" % test_form2.validate())
print("       Errors: %s" % test_form2.errors)

HTML Render 2: <input id="num" name="num" type="text" value="66">
     Validate: True
       Errors: {}
查看更多
登录 后发表回答