This blog is now hosted at consciou.us
Showing posts with label math is hard. Show all posts
Showing posts with label math is hard. Show all posts

Friday, February 26, 2010

Numeric Formatting in Django

You might have seen my previous post lamenting the weirdnesses around number formatting in python.  This spills over into Django; if you have DecimalFields in your models (especially if you are using the ModelForm object to create your forms.


The previous "format Decimal object as a string that any 10-year-old would expect" method is:

def dec_string(dec):
    if isinstance(dec, Decimal):
        if dec:
            if dec.normalize().as_tuple().exponent > 0:
                return "%d"%dec
            else:
                return "%s"%dec.normalize()
        else:
            return 0
    else:
        return dec
(I added the isinstance() bit to protect me from myself =)

Since we know how to format this, all that remains to be done is to create a widget that automatically formats DecimalFields correctly.


class DecimalInput(TextInput):
  def render(self, name, value, attrs=None):
    value = dec_string(value)
    return super(DecimalInput, self).render(name, value, attrs)
And then just use that in your form:

class EnergyForm(ModelForm):
    kwh = DecimalField(widget=DecimalInput(attrs={'size':'6'}))

There is more to the ModelForm, but that's what the docs are for, right? Read more...

Numeric Formatting in Python

Sometimes I hate Python. Here's the difference between Perl and Python: Perl accepts, even embraces the fact that language is messy. Python sometimes gets this wrong.

Here are some samples of dealing with formatting numbers:

from decimal import Decimal

>>> f= Decimal('1200.1')
>>> "%s"%f.normalize()
'1200.1'
Huzzah, it does the right thing!

>>> f= Decimal('1200')
>>> "%s"%f.normalize()
'1.2E+3'
 Doh!  But clearly, removing the normalize() will fix things, right?

>>> "%s"%f
'1200'
Yee-haw! Now we're cooking!

>>> f= Decimal('1200.100')
>>> "%s"%f
'1200.100'
Hm, users don't want the trailing zeroes.  The only thing that I can think to do is use a regular expression, and now I have two problems.

 But wait! If the number doesn't have anything after the decimal, then we do one, and if it does, we do the other!

def dec_string(dec):
    if dec:
        if dec.normalize().as_tuple().exponent > 0:
            return "%d"%dec
        else:
            return "%s"%dec.normalize()
    else:
        return 0
 And that seems to be working.  Note that the final version uses "%d"%dec, rather than %s, since we want to make sure and truncate past the decimal.

Update: check out my post about integrating it with Django forms to make it seamless. Read more...