views:

256

answers:

2

I am writing a web2py application that requires summing dollar amounts without losing precision. I realize I need to use Decimals for this, but I've found myself having to wrap every single number I get from the database with:

Decimal(str(myval))

Before I go crazy adding that to all of my code, is there a better way? I'm new to Python, so it's very possible that I am overlooking something obvious.

Edit: My database is MS SQL Server and I'm storing the amounts in SQL Server money fields (I believe the implementation is analogous to Decimal, ie integer math, not floating point).

I am connecting to the db through the web2py framework (which uses pyodbc for SQL Server connections). I believe web2py has some support for decimal types. For example, my web2py field definitions look like: Field('Amount','decimal(19,4)') However, when I return a value from the database using web2py's .executesql method it returns the value as a float and not a Decimal.

Edit: This appears to be an issue with FreeTDS and MS SQL Server. As Massimo stated in the comments, web2py supports this properly and returns a Decimal (if it can). It turns out this is only an issue in my production environment (Linux). I am using the FreeTDS driver to connect to MS SQL and it appears to be translating the MS SQL money type to a python float.

I think Alex Martelli's answer is pointing in the right direction. Does anyone have any experience with FreeTDS, MS SQL, and python? I think this probably warrants its own question, so I'll move this discussion... (new question posted here: FreeTDS translating MS SQL money type to python float, not Decimal)

A: 

You could write a function which both gets the number from the database and then converts it to decimal. Then just use that in your code.

Alex Bliskovsky
+6  A: 

First, keep all numbers in decimal form in the database -- you don't mention what DB engine you're using, but every engine supports such functionality, e.g., here is MySQL's DECIMAL type documentation. I hope you're already doing that, but, if you aren't, it's still worth the pain of a schema change.

Then, you need to ensure the type conversion to and from the DB matches the database's decimals with Python's. Unfortunately there is no standard way to do it across DB-API implementations, but most do offer a way; for example, with MySQLDB you need to pass a conv= parameter in connect, known as a "type converters dictionary" -- the default is a copy of MySQLdb.converters.conversions but of course you can enrich it to do the right thing with decimals. If you tell us which DB you're using, and what DB-API connector package you prefer for it, we may be able to give you more detailed help on this subject.

Edit: @unutbu remarks in a comment that the current MySQLdb converter already does translate decimals correctly, so, unless you're stuck with a very old release of MySQLdb, you should be fine as long as you do correctly keep all the numbers as decimal in the DB (yay!).

Alex Martelli
MySQLdb's default converter `MySQLdb.converters.conversions` automatically converts data of MySQL Decimal type to Python decimal.Decimals.
unutbu
@unutbu, that's good news -- it didn't when I last used MySQLdb, but then that was a pretty old release and might have needed to avoid such conversion to support very old Python releases.
Alex Martelli
Is there a pyodbc equivalent to the MySQLDB conv= parameter and MySQLdb.converters.conversions?
mwolfe02