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をレンダリングできるようにします。

私の現在の解決策は、のTextField代わりにを使用し、バリデーターに次のような呼び出しメソッドをDecimalField提供することです。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) のカスタム サブクラスを作成し、必要に応じて、、、およびメソッドをオーバーライド__call__するprocess_dataことprocess_formdataです。__call__それらをhtmlにレンダリングするためのもので(別のwidgetcallableによって行われる必要があります)、process_data通常はpythonオブジェクトからデータを取得するためのもので(サーバー側を意味します)、ユーザーが送信したフォームを処理することを除いてprocess_formdata似ています。process_dataまたは、DRY が必要な場合 (自分で繰り返さないでください)、オーバーライド__init__してカスタムバリデーターをデフォルトにすることができます。

編集:

DecimalFieldばかげたコードが原因TypeErrorです。のレンダリング ウィジェットが X を取得するために_value()使用するメソッドを見てみましょう。DecimalField<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 に答える