Bitbucket is a code hosting site with unlimited public and private repositories. We're also free for small teams!

Close

Overview

python-money provides carefully designed basic Python primitives for working with money and currencies.

The need to represent instances of money frequently arises in software development, particularly any financial/economics software. The py-moneyed package provides the classes of Money and Currency to help with that need, at a level more useful than just Python's Decimal class, or ($DEITY forbid) the float primitive. The package is meant to be stand-alone and easy to either use directly, or subclass further.

The primary objectives of this module is to aid in the development of financial applications by increasing testability and reusability, reducing code duplication and reducing the risk of defects occurring in the code.

The module defines two basic Python classes -- a Currency class and a Money class. It also pre-defines all the world's currencies, according to the ISO 4217 standard. The classes define some basic operations for working with money, overriding Python's addition, substraction, multiplication, etc. in order to account for working with money in different currencies. They also define currency-aware comparison operators. To avoid floating point precision errors in monetary calculations, the module uses Python's Decimal type exclusively.

The design of the module is based on the Money enterprise design pattern, as described in Martin Fowler's "Patterns of Enterprise Application Architecture".

This project also contains Django helper classes for easy integration with python-money.

Installation

You can install this project from PyPI:

$ pip install python-money

Usage

On to the code: the Money class is instantiated with:

  • An amount which can be of type string, float, or Decimal.
  • A currency, which usually is specified by the three-capital-letters ISO currency code,e.g. USD.

For example,

from moneyed.classes import Money
sale_price_today = Money(amount='99.99', currency='USD')

The Money class also provides operators with type checking, matching currency checking, and sensible dimensional behavior, e.g. you cannot multiply two Money instances, nor can you add a Money instance to a non-Money number; dividing a Money instance by another results in a Decimal value, etc.

The Currency class is provided with a complete dictionary of ISO 4217 currencies data, each key (e.g. 'USD') mapping to a Currency instance with ISO numeric code, canonical name in English, and countries using the currency. Thanks to the python-money developers for their (possibly tedious) data-entry of the ISO codes!

This application contains several classes and functions that make dealing with money easier and less error prone.

Currency Types

The Currency class can be used to represent a type of Currency. It contains values for the currency's code, ISO number, name and the country it's used in.

For example:

Currency(code='BZD', numeric='084', name='Belize Dollar', countries=['BELIZE'])

There is a dict of all ISO-4217 currencies:

>>> from money import CURRENCY
>>> print CURRENCY['GBP'].name
Pound Sterling

Money Class

The Money class is available for doing arithmetic on values in defined currencies. It wraps the python Decimal type and gives you several convienience methods. Using this prevents you from making mistakes like adding Pounds and Dollars together, multiplying two money values or comparing different currencies. For example:

>>> usd = Money(amount=10.00, currency=CURRENCY['USD'])
>>> print usd
USD 10.00

>>> jpy = Money(amount=2000, currency=CURRENCY['JPY'])
>>> print jpy
JPY 2000.00

>>> print jpy * usd
Traceback (most recent call last):
  File "<console>", line 1, in <module>
  File "/python-money/money/Money.py", line 79, in __mul__
    raise TypeError, 'can not multiply monetary quantities'
TypeError: can not multiply monetary quantities

>>> print jpy > usd
Traceback (most recent call last):
  File "<console>", line 1, in <module>
  File "/Users/poswald/Projects/python-money/money/Money.py", line 137, in __gt__
    raise TypeError, 'can not compare different currencies'
TypeError: can not compare different currencies

>>> print 1 % usd
USD  0.10

>>> print usd * 5
USD 50.00

>>> print (usd * 5).allocate((50,50))
[USD 25.00, USD 25.00]
>>> print (jpy * 5).allocate((50,50))
[JPY 5000.00, JPY 5000.00]

Default Currency

This package assumes that you have a preferred default currency. Somewhere in your software's initialization you should set that currency:

>>> from money import set_default_currency
>>> set_default_currency(code='USD')
>>> print Money(amount=23.45)
USD 23.45

If you don't you will get a non-specified 'XXX' currency:

>>> print Money(amount=23.45)
XXX 23.45

There is also an exchange rate that may be set:

This default currency and exchange rate is used for arithmetic addition. If you add two monetary values that are in differing currency, they will first be converted into the default currency, and then added together.

Testing

TODO

Django

This package includes some classes as a convenience to Django users. These are entirely optional.

Model Field

Add a currency field to your models. This field takes similar parameters as the Django DecimalField:

from money.contrib.django.models.fields import MoneyField

class Thing(models.Model):
    ...
    price = MoneyField(default=0, max_digits=12, decimal_places=2)
    ...

Now run ./manage.py dbsync or South migrate. Your database table will contain a field for holding the value and a second field for the currency type. In postgresql it might look like this:

price          | numeric(12,2)          | not null default NULL::numeric
price_currency | character varying(3)   | not null

The value you get from your model will be a Money class:

thing = Thing.objects.get(id=123)
print repr(thing.price)
USD  199.99

Form Field

The form field used by the models.MoneyFiel is also called MoneyField in

Currencies

django-currencies allows you to define different currencies, and includes template tags/filters to allow easy conversion between them.

You can use the following code in your templates:

{% change_currency [price] [currency_code] %}

# i.e:

{% change_currency product.price "USD" %}

# or if we have the currencies.context_processors.currencies
# available:

{% change_currency product.price CURRENCY.code %}

or use the filter:

{{ [price]|currency:[currency] }}

# i.e.:

{{ product.price|currency:"USD" }}

or set the CURRENCY context variable with a POST to the included view:

{% url currencies_set_currency [currency] %}

Running Django Tests

There are some test cases included for the Django types. If you want to run them, add the test application to your INSTALLED_APPS:

INSTALLED_APPS = (
...
'money.tests',
...
)

Run them with the manage command from your application:

$ ./manage.py test money
Creating test database 'default'...

...

Ran 8 tests in 0.445s

OK
Destroying test database 'default'...
$

Recent activity

Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.