Source code for wtforms.fields.numeric

import decimal

from wtforms import widgets
from wtforms.fields.core import Field
from wtforms.utils import unset_value

__all__ = (
    "IntegerField",
    "DecimalField",
    "FloatField",
    "IntegerRangeField",
    "DecimalRangeField",
)


class LocaleAwareNumberField(Field):
    """
    Base class for implementing locale-aware number parsing.

    Locale-aware numbers require the 'babel' package to be present.
    """

    def __init__(
        self,
        label=None,
        validators=None,
        use_locale=False,
        number_format=None,
        **kwargs,
    ):
        super().__init__(label, validators, **kwargs)
        self.use_locale = use_locale
        if use_locale:
            self.number_format = number_format
            self.locale = kwargs["_form"].meta.locales[0]
            self._init_babel()

    def _init_babel(self):
        try:
            from babel import numbers

            self.babel_numbers = numbers
        except ImportError as exc:
            raise ImportError(
                "Using locale-aware decimals requires the babel library."
            ) from exc

    def _parse_decimal(self, value):
        return self.babel_numbers.parse_decimal(value, self.locale)

    def _format_decimal(self, value):
        return self.babel_numbers.format_decimal(value, self.number_format, self.locale)


[docs]class IntegerField(Field): """ A text field, except all input is coerced to an integer. Erroneous input is ignored and will not be accepted as a value. """ widget = widgets.NumberInput() def __init__(self, label=None, validators=None, **kwargs): super().__init__(label, validators, **kwargs) def _value(self): if self.raw_data: return self.raw_data[0] if self.data is not None: return str(self.data) return "" def process_data(self, value): if value is None or value is unset_value: self.data = None return try: self.data = int(value) except (ValueError, TypeError) as exc: self.data = None raise ValueError(self.gettext("Not a valid integer value.")) from exc def process_formdata(self, valuelist): if not valuelist: return try: self.data = int(valuelist[0]) except ValueError as exc: self.data = None raise ValueError(self.gettext("Not a valid integer value.")) from exc
[docs]class DecimalField(LocaleAwareNumberField): """ A text field which displays and coerces data of the `decimal.Decimal` type. :param places: How many decimal places to quantize the value to for display on form. If None, does not quantize value. :param rounding: How to round the value during quantize, for example `decimal.ROUND_UP`. If unset, uses the rounding value from the current thread's context. :param use_locale: If True, use locale-based number formatting. Locale-based number formatting requires the 'babel' package. :param number_format: Optional number format for locale. If omitted, use the default decimal format for the locale. """ widget = widgets.NumberInput(step="any") def __init__( self, label=None, validators=None, places=unset_value, rounding=None, **kwargs ): super().__init__(label, validators, **kwargs) if self.use_locale and (places is not unset_value or rounding is not None): raise TypeError( "When using locale-aware numbers, 'places' and 'rounding' are ignored." ) if places is unset_value: places = 2 self.places = places self.rounding = rounding def _value(self): if self.raw_data: return self.raw_data[0] if self.data is None: return "" if self.use_locale: return str(self._format_decimal(self.data)) if self.places is None: return str(self.data) if not hasattr(self.data, "quantize"): # 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 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 str(quantized) def process_formdata(self, valuelist): if not valuelist: return try: if self.use_locale: self.data = self._parse_decimal(valuelist[0]) else: self.data = decimal.Decimal(valuelist[0]) except (decimal.InvalidOperation, ValueError) as exc: self.data = None raise ValueError(self.gettext("Not a valid decimal value.")) from exc
[docs]class FloatField(Field): """ A text field, except all input is coerced to an float. Erroneous input is ignored and will not be accepted as a value. """ widget = widgets.TextInput() def __init__(self, label=None, validators=None, **kwargs): super().__init__(label, validators, **kwargs) def _value(self): if self.raw_data: return self.raw_data[0] if self.data is not None: return str(self.data) return "" def process_formdata(self, valuelist): if not valuelist: return try: self.data = float(valuelist[0]) except ValueError as exc: self.data = None raise ValueError(self.gettext("Not a valid float value.")) from exc
[docs]class IntegerRangeField(IntegerField): """ Represents an ``<input type="range">``. """ widget = widgets.RangeInput()
[docs]class DecimalRangeField(DecimalField): """ Represents an ``<input type="range">``. """ widget = widgets.RangeInput(step="any")