Django Modelform DecimalField drop trailing 0s for display, up to 2 decimal places

70 views Asked by At

I have a Modelform that contains multiple DecimalFields with decimal_places=4. I need 4 decimal places as that is a reflection of the underlying data. This is a financial utility where mean values might consume all 4 decimal places. Users should be allowed to enter up to 4 decimal places, and forms should be displayed with up to 4 decimal places. I am not looking for any sort of rounding or truncation.

What I want to do is limit the display of trailing 0s in modelforms being rendered in my template, up to a minimum of 2 trailing 0s, as these are dollars. For instance:

3.1234 would still remain as 3.1234
3.1230 would become 3.123
3.1200 would become 3.12
3.1000 would become 3.10
3.0000 would become 3.00

I have came up with the following inside my modelforms init method:

def __init__(self, *args, **kwargs):
    if self.initial:
        for field_name, field in self.fields.items():
            if isinstance(field, forms.DecimalField) and field.decimal_places == 4 and self.initial.get(field_name):
                self.initial[field_name] = self.initial[field_name].normalize()

Using the .normalize() drops ALL the trailing 0s (3.0000 -> 3), however, I want a minimum of 2. I thought of using .quantize(), to enforce a minimum of 2 decimal places, but this rounds values with more then 2 decimal places, which I do not want. I don't think I can use template filters, as I would need to render the form values separately. I am rendering my form fields as {{ form.fieldname }}.

If anyone can think of a solution for this, it would be much appreciated. <3

1

There are 1 answers

1
Daniel Boctor On BEST ANSWER

I ended up writing my own widget and applying it to the applicable fields in my form. Hope this helps if anyone else was experiencing this.

class DollarDisplayTextInput(forms.TextInput):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

    def format_value(self, value):
        if value is not None and isinstance(value, Decimal):
        #Strip trailing 0s, leaving a minimum of 2 decimal places
            while (abs(value.as_tuple().exponent) > 2 and value.as_tuple().digits[-1] == 0):
                value = Decimal(str(value)[:-1])
        return value