1

看看这个例子

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

而不是这种行为,我希望该字段自身添加一个错误,以便我仍然可以渲染 t.

我当前的解决方案涉及使用 aTextField而不是 aDecimalField并为IsNumeric验证器提供如下调用方法:

def __call__(self, form, field):
    if field.data:
        try:
            field.data = float(field.data)
        except ValueError as e:
            raise ValidationError(self.message)

这几乎完美地工作,并且是我目前的解决方案,但肯定必须有一种 wtforms 惯用的方式来做到这一点。

4

1 回答 1

2

我不得不处理类似的问题。SelectMultipleFieldwith coerce=text_type(这是默认的 kwarg)将输入数据转换为str/ unicode。这意味着它“强制” None(当用户没有选择任何内容时输入我的字段的值)进入"None"- 这是调试的巨大痛苦。您的问题清楚地表明,不仅仅是我参与了 wtforms 内部验证过程。

我找到的正确解决方案是,您编写一个自定义子类StringField(textfield) 和 override __call__, process_data, 和process_formdata方法以满足您的需求。__call__用于将它们呈现为 html(应由另一个widget可调用对象完成),process_data通常用于从 python 对象(这意味着服务器端)获取数据,除了它处理用户提交的表单外,其他process_formdata类似。process_data或者,如果您想要 DRY(不要重复自己),您可以覆盖__init__并使您的自定义验证器成为默认值。

编辑:

DecimalField白痴代码是什么原因造成的TypeError。看一下它的_value()方法,它被DecimalField的渲染小部件用来获取 X 的<input type="text" ... value=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的类型之前进行检查,并且只是说您需要 N 位精度;它并不表示您有 N 位作为您的,但是 wtforms 开发人员以某种方式决定检查足以假设该字段包含相应的浮点数。(代码检查是否有属性,但仅适用于小数)field.datafield.placesDecimalfields.datafield.placesquantizefield.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 ''
于 2013-03-12T16:33:14.447 回答