Thread: How to use the postgresql money type?

How to use the postgresql money type?

From
"W. Matthew Wilson"
Date:
PostgreSQL has a money type, but I don't know how to use it with
psycopg2.  Do I need  to write my own code to convert to and from SQL?

Matt

--
W. Matthew Wilson
matt@tplus1.com
http://tplus1.com

Re: How to use the postgresql money type?

From
Daniele Varrazzo
Date:
On Wed, Jan 19, 2011 at 8:06 PM, W. Matthew Wilson <matt@tplus1.com> wrote:
> PostgreSQL has a money type, but I don't know how to use it with
> psycopg2.  Do I need  to write my own code to convert to and from SQL?

From the docs I read that the output format is system and locale
dependent, so it doesn't seem easy or doable at all to add support in
psycopg in a general way.

In a more specific way instead, if you know the database locale and
you know e.g. that the symbol is "$" and the separator is ",", you can
write a typecaster for your database. Here is how to write a
typecaster to convert from money to Python Decimal, and to register it
on a specific connection.

    In [1]: import psycopg2

    In [2]: cnn = psycopg2.connect("dbname=test user=postgres")

    In [3]: from decimal import Decimal

    In [4]: def cast_money(s, cur):
       ...:     if s is None: return None
       ...:     return Decimal(s.replace(",","").replace("$",""))
       ...:

    In [5]: MONEY = psycopg2.extensions.new_type((790,), "MONEY", cast_money)

    In [6]: psycopg2.extensions.register_type(MONEY, cnn)

    In [7]: cur = cnn.cursor()

    In [8]: cur.execute("select '1000'::money;")

    In [9]: cur.fetchone()
    Out[9]: (Decimal('1000.00'),)

I also see that PostgreSQL doesn't let you convert from decimal to
money: bad stuff

    test=> select 1000.00::money;
    ERROR:  cannot cast type numeric to money
    LINE 1: select 1000.00::money;
                          ^

This means that you also need an adapter to represent a monetary
amount as a string literal (in quotes). Adapters can only be
registered globally, not per connection, so it is less optimal than in
the other direction: you either overwrite the Decimal adapter or you
can use a different Python class to represent monies (e.g. a Decimal
subclass). An example overwriting Decimal is:

    In [10]: class MoneyAdapter:
       ....:         def __init__(self, m):
       ....:             self.m = m
       ....:         def getquoted(self):
       ....:             return
psycopg2.extensions.adapt(str(self.m)).getquoted()
       ....:

    In [11]: psycopg2.extensions.register_adapter(Decimal, MoneyAdapter)

    In [12]: cur.mogrify("select %s;", (Decimal(1000),))
    Out[12]: "select '1000';"


-- Daniele

Re: How to use the postgresql money type?

From
"A.M."
Date:
On Jan 19, 2011, at 6:42 PM, Daniele Varrazzo wrote:

> On Wed, Jan 19, 2011 at 8:06 PM, W. Matthew Wilson <matt@tplus1.com> wrote:
>> PostgreSQL has a money type, but I don't know how to use it with
>> psycopg2.  Do I need  to write my own code to convert to and from SQL?
>
>
> I also see that PostgreSQL doesn't let you convert from decimal to
> money: bad stuff

That's because non-integer numbers in postgresql are interpreted as floats, which is exactly what you do not want to
representmoney. It's the same in python: 

>>> from decimal import Decimal
>>> Decimal(100.00)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/decimal.py", line 652, in __new__
    "First convert the float to a string")
TypeError: Cannot convert float to Decimal.  First convert the float to a string

So the example which accepts floats and converts the input to a string includes a critical bug, not a workaround.
Becausethe money datatype is locale-dependent, the type should effectively be treated as an opaque text value- the
valuein the database must be used as-is for display to the user (including the decimal delimiter and currency
indicator).Math on money must be done in the database- this is required to properly handle banker's rounding and other
moneygimmicks. 

For the original poster, if these restrictions are in your way, then look at NUMERIC(precision,scale) for an effective
equivalentto python's Decimal class. 

Cheers,
M

Re: How to use the postgresql money type?

From
Federico Di Gregorio
Date:
On 20/01/2011 16:40, A.M. wrote:
>
> On Jan 19, 2011, at 6:42 PM, Daniele Varrazzo wrote:
>
>> On Wed, Jan 19, 2011 at 8:06 PM, W. Matthew Wilson <matt@tplus1.com> wrote:
>>> PostgreSQL has a money type, but I don't know how to use it with
>>> psycopg2.  Do I need  to write my own code to convert to and from SQL?
>>
>>
>> I also see that PostgreSQL doesn't let you convert from decimal to
>> money: bad stuff
>
> That's because non-integer numbers in postgresql are interpreted as floats,
> which is exactly what you do not want to represent money. It's the
same in python:

decimal is not float.

> So the example which accepts floats and converts the input to a string includes a critical bug,
> not a workaround. Because the money datatype is locale-dependent, the type should effectively
> be treated as an opaque text value- the value in the database must be used as-is for display
> to the user (including the decimal delimiter and currency indicator).
Math on money must be
> done in the database- this is required to properly handle banker's rounding and other money
> gimmicks.

But this makes sense. Do you mean that banker's rounding may depend on
the locale? If that's the case and if PostgreSQL implements such
gimmicks then the correct default for money would be to return it to
Python as string and let the user decide how to proceed from there.

federico

--
Federico Di Gregorio                         federico.digregorio@dndg.it
Studio Associato Di Nunzio e Di Gregorio                  http://dndg.it
 Degli altri, della gente senza domande, si puo' fare a meno.
                                                       -- macchinavapore

Re: How to use the postgresql money type?

From
"A.M."
Date:
On Jan 20, 2011, at 10:45 AM, Federico Di Gregorio wrote:

> On 20/01/2011 16:40, A.M. wrote:
>>
>
> But this makes sense. Do you mean that banker's rounding may depend on
> the locale? If that's the case and if PostgreSQL implements such
> gimmicks then the correct default for money would be to return it to
> Python as string and let the user decide how to proceed from there.

Banker's rounding doesn't occur at all by default with the money type since it doesn't represent values outside it's
locale-dependentscale, but any real->money conversion must occur in the database to account for this possibility.  

> the type should effectively
> be treated as an opaque text value

is what I wrote above. Python can't really do anything with the value other than to display it.

Cheers,
M

Re: How to use the postgresql money type?

From
Adrian Klaver
Date:
On Thursday 20 January 2011 7:45:30 am Federico Di Gregorio wrote:
> On 20/01/2011 16:40, A.M. wrote:

>
> But this makes sense. Do you mean that banker's rounding may depend on
> the locale? If that's the case and if PostgreSQL implements such
> gimmicks then the correct default for money would be to return it to
> Python as string and let the user decide how to proceed from there.
>
> federico

Info on the money type:
http://www.postgresql.org/docs/9.0/interactive/datatype-money.html

FYI this type at times has been marked as deprecated. There has been some recent
work on it but it has issues especially, from the link above, the dump output
and moving data to a different locale.

--
Adrian Klaver
adrian.klaver@gmail.com