Login

Currency Fields with newforms

Author:
sago
Posted:
April 13, 2007
Language:
Python
Version:
.96
Score:
4 (after 4 ratings)

The newforms package allows you to simply add new field types to your forms.

This snippet shows the addition of a new type of field, to make sure the user enters sensible currency values (either with no decimal, or two-decimal places), and a custom widget to make sure that the value is displayed correctly when the user sees the form.

The CurrencyInput widget simply tries to display the current value in floating point 2-decimal places format (and gives up if it can't). The CurrencyField makes sure that the input value matches a regular expression, and when it is asked to clean the value it converts it to a float.

The regex here limits the amount to 99,999.99 or less. This is within the bounds of accuracy of python's float value. If you want to use truly huge values for the amount, then you'll face problems with the floating point not being able to represent the values you enter, and so the conversion to floating point in the field will fail. In this case it would be better to use python longs, and used a fixed point interpretation.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import re
from django import newforms as forms

class CurrencyField (forms.RegexField):
    currencyRe = re.compile(r'^[0-9]{1,5}(.[0-9][0-9])?$')
    def __init__(self, *args, **kwargs):
        super(CurrencyField, self).__init__(
            self.currencyRe, None, None, *args, **kwargs)

    def clean(self, value):
        value = super(CurrencyField, self).clean(value)
        return float(value)

class CurrencyInput (forms.TextInput):
    def render(self, name, value, attrs=None):
        if value != '':
            try:
                value = u"%.2f" % value
            except TypeError:
                pass
        return super(CurrencyInput, self).render(name, value, attrs)

class MyForm (forms.Form):
    amount = CurrencyField(widget=CurrencyInput, initial=5)

More like this

  1. Template tag - list punctuation for a list of items by shapiromatron 10 months, 1 week ago
  2. JSONRequestMiddleware adds a .json() method to your HttpRequests by cdcarter 10 months, 2 weeks ago
  3. Serializer factory with Django Rest Framework by julio 1 year, 5 months ago
  4. Image compression before saving the new model / work with JPG, PNG by Schleidens 1 year, 6 months ago
  5. Help text hyperlinks by sa2812 1 year, 6 months ago

Comments

afternoon (on April 13, 2007):

Looks good, but is Decimal a better choice than float?

#

sago (on April 17, 2007):

Yes you could replace line 12 with

return decimal.Decimal(value)

but to take advantage of that extra accuracy, you'd need to use IntegerField in the database and do fixed-point conversion when you read and write it.

As it stands above (assuming a FloatField database column) the Decimal will buy you nothing, as the value is returned via float() by django, so you'll run out of accuracy at the same point.

As a general rule, however, it is better to use Decimal than float in python when dealing with currency.

#

dyadya-zed (on April 18, 2007):

May be it would better to use NumPy (it is scientific python extension for very long integer numbers and operations between them)

#

akaihola (on January 10, 2008):

You could reverse the order in which CurrencyField and CurrencyInput are defined, and add

widget = CurrencyInput

to the definition of CurrencyField.

This way there's no need to define the form field's widget class explicitly.

#

akaihola (on January 10, 2008):

Easy internationalization: to handle both dot and comma as a decimal separator, use this regex:

currencyRe = re.compile(r'^[0-9]{1,5}([,\.][0-9][0-9])?$')

Replace CurrencyField's clean() return value with

return float(value.replace(',', '.'))

If you want to display commas instead of dots in forms by default, replace CurrencyInput's try clause with

value = (u"%.2f" % value).replace('.', ',')

#

zejn (on April 4, 2011):

You really really really should't use float for monetary values. In some cases it's even illegal, because float loses precision and with money that's a no-no.

#

Please login first before commenting.