Using sqlite3 as the database, I want to keep a couple of decimal.Decimal values, one a percentage the other a dollar amount. However I saw problems when using sum(amount) on over 20,000 entries. It was several dollars off.
I thought of using an adapter to save the number of cents and then aggregates should work.
sqlite3.register_adapter(decimal.Decimal, lambda x:str(x))
sqlite3.register_adapter(decimal.Decimal, lambda x:int(x*100))
However, I would now need two classes as I can not use the same class. Trying to subclass Decimal became an issue as it uses Decimal within itself. Fine. I'll just copy decimal.py and replace every occurance of Decimal|decimal with Money|money.
$ copy decimal.py money.py $ sed -e "s/Decimal/Money/g" -e "s/decimal/money/g" -i money.py $ money.py
All the unit tests work. I try it now and I am getting a "probably unsupported type" error. I change around the converters, use basic types. I just can not get it working right and I don't have a better idea for the problem.
I need some help and have a sample below. Ideas on how to get it working or a better solution using standard libraries?
import decimal
import money
import sqlite3
sqlite3.register_adapter(decimal.Decimal, lambda x:str(x))
sqlite3.register_converter('decimal', decimal.Decimal)
sqlite3.register_adapter(money.Money, lambda x: int(value*100))
sqlite3.register_converter('intcents', lambda x: money.Money(x)/100)
conn = sqlite3.connect(":memory:",
detect_types=sqlite3.PARSE_DECLTYPES|sqlite3.PARSE_COLNAMES)
cursor = conn.cursor()
cursor.executescript("CREATE TABLE example(rate decimal, amount intcents)")
for x in (1,1,1,2,2,2,3,3,3):
rate = decimal.Decimal(str(x))/100
amount = money.Money(str(rate))
try:
cursor.execute("INSERT INTO example VALUES(?,?)", (rate, amount))
except:
print (rate, amount)
raise
cursor.execute("""SELECT sum(rate), sum(amount) FROM example""")
print cursor.fetchone()
cursor.execute("""SELECT sum(rate) as "t [decimal]", sum(amount)as "a [intcents]"FROM example""")
print cursor.fetchone()
There are two simple solutions, and I hope someone has something more direct:
1. convert to a string and store the string in the DB
2. multiply by 100 and store as an integer for money values
Both of these will require a conversion back to decimal.Decimal when the values are read from the DB.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With