Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Decimal field rounding in WTForms

I have a form that contains a price decimal field, like so:

from flask.ext.wtf import Form
import wtforms
from wtforms.validators import DataRequired
from decimal import ROUND_HALF_UP

class AddListingBase(Form):
    title = wtforms.StringField(validators=[DataRequired()])
    details = wtforms.TextAreaField(validators=[DataRequired()])
    price = wtforms.DecimalField(places=2, rounding=ROUND_HALF_UP, validators=[DataRequired()])

When I submit the form, the decimal value is suppoosed to be rounded to 2 decimal places, but that never happens. I always get the value as it was specified (e.g. 99.853 is 99.853, not 99.85 as it should be).

like image 263
KRTac Avatar asked Jan 05 '15 14:01

KRTac


1 Answers

As @mueslo has rightly inferred, this is because the default DecimalField implementation does not round off the form data it receives. It only rounds off the initial data (as in defaults, or model/saved data).

We can easily change this behavior with a modified DecimalField implementation, wherein we override the process_formdata method. Somewhat like so:

from wtforms import DecimalField


class BetterDecimalField(DecimalField):
    """
    Very similar to WTForms DecimalField, except with the option of rounding
    the data always.
    """
    def __init__(self, label=None, validators=None, places=2, rounding=None,
                 round_always=False, **kwargs):
        super(BetterDecimalField, self).__init__(
            label=label, validators=validators, places=places, rounding=
            rounding, **kwargs)
        self.round_always = round_always

    def process_formdata(self, valuelist):
        if valuelist:
            try:
                self.data = decimal.Decimal(valuelist[0])
                if self.round_always and 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)
                    self.data = quantized
            except (decimal.InvalidOperation, ValueError):
                self.data = None
                raise ValueError(self.gettext('Not a valid decimal value'))

Example usage:

rounding_field = BetterDecimalField(round_always=True)

Gist

like image 116
bool.dev Avatar answered Sep 23 '22 13:09

bool.dev