看一下这个例子
class MyForm(ForM):
...snip...
quantity = DecimalField(u'Quantity', [NumberRange(1, 8)])
...snip...
如果用户合作并输入可以强制转换为数字类型的内容,则可以很好地工作。但是,如果用户在浏览器的此字段中输入“asdf”,那么当我尝试在浏览器中呈现DecimalField
时,Type Error
会抛出Traceback (most recent call last):
File "/path/to/app/venv/local/lib/python2.7/site-packages/jinja2/environment.py", line 894, in render
return self.environment.handle_exception(exc_info, True)
File "/path/to/app/www/templates/submodule/user/submission.html", line 26, in block "submodule_content"
{{form.quantity}}
File "/path/to/app/venv/local/lib/python2.7/site-packages/wtforms/fields/core.py", line 139, in __call__
return self.widget(self, **kwargs)
File "/path/to/app/venv/local/lib/python2.7/site-packages/wtforms/widgets/core.py", line 123, in __call__
kwargs['value'] = field._value()
File "/path/to/app/venv/local/lib/python2.7/site-packages/wtforms/fields/core.py", line 542, in _value
return format % self.data
TypeError: a float is required
。
以下是追溯的相关部分:
TextField
而不是这种行为,我希望该字段有一个错误添加到自身,以便我仍然可以渲染t。
我目前的解决方案是使用DecimalField
代替IsNumeric
并使用以下调用方法提供def __call__(self, form, field):
if field.data:
try:
field.data = float(field.data)
except ValueError as e:
raise ValidationError(self.message)
验证程序:
{{1}}
这几乎完美无缺,并且是我目前的解决方案,但肯定必须有一种重量级的惯用方法来实现这一目标。
答案 0 :(得分:2)
我不得不处理类似的问题。 SelectMultipleField
coerce=text_type
(默认为kwarg)会将输入数据转换为str
/ unicode
。这意味着它被强迫了#39; None
(当用户没有选择任何内容时,我的字段中输入的值)到"None"
- 这是调试的巨大痛苦。你的问题清楚地表明,不仅是我参与了整个内部验证过程。
我找到的正确解决方案是,您编写StringField
(textfield)的自定义子类,并覆盖__call__
,process_data
和process_formdata
方法以满足您的需求。 __call__
用于将它们呈现为html(应该由另一个widget
可调用来完成),process_data
通常用于从python对象(这意味着服务器端)获取数据,process_formdata
1}}与process_data
类似,但它处理用户提交的表单。或者,如果你想要干(不要自己重复),你可以覆盖__init__
并使自定义验证器成为默认值。
编辑:
DecimalField
的愚蠢代码是导致TypeError
的原因。查看_value()
方法,DecimalField
的渲染小部件使用该方法获取<input type="text" ... value=X>
的X
def _value(self):
if self.raw_data:
return self.raw_data[0]
elif self.data is not None:
if self.places is not None: # <-- this line brings you the pain
if hasattr(self.data, 'quantize'):
exp = decimal.Decimal('.1') ** self.places
if self.rounding is None:
quantized = self.data.quantize(exp)
else:
quantized = self.data.quantize(exp, rounding=self.rounding)
return text_type(quantized)
else:
# If for some reason, data is a float or int, then format
# as we would for floats using string formatting.
format = '%%0.%df' % self.places
return format % self.data
else:
return text_type(self.data)
else:
return ''
它在创建字段时声明field.places
- field.data
的类型之前检查field.places
,并且只是说你需要N位精度;它并不表示您有Decimal
的N位数作为fields.data
,但wtforms devs以某种方式决定检查field.places
足以假设该字段包含相应的浮点数。 (代码检查quantize
上是否有field.data
属性,但仅适用于小数)
我认为你必须编写自己的DecimalField
子类。这是我尝试编写DecimalField
的非白痴版本
from wtforms.fields.core import DecimalField as _DecimalField
class DecimalField(_DecimalField):
def _value(self):
try:
float(self.data) #check whether you have a 'number'
return super(DeciamlField, self)._value()
except (TypeError, ValueError): #self.data is 'None', 'asdf' ...
return text_type(self.data) if self.data else ''