I have an instance of decimal.Decimal
which comes from an SQLAlchemy query. As I need to serialize the object, I have created a JSON serializer to deal with the Decimal
:
import decimal
class AlchemyEncoder(json.JSONEncoder):
def default(self, obj):
if isinstance(obj, decimal.Decimal):
return str(obj)
return json.JSONEncoder.default(self, obj)
The unfortunate thing is that the isinstance(obj, decimal.Decimal)
does not return a True
for the instance, even though (using pdb in the default
method above):
obj.__class__ # => <class 'decimal.Decimal'>
blah = decimal.Decimal()
blah.__class__ # => <class 'decimal.Decimal'>
isinstance(obj, decimal.Decimal) # => False
isinstance(blah, decimal.Decimal) # => True
isinstance(obj, obj.__class__) # => True
I did check that the module both instances refer to is the same module:
import inspect
inspect.getfile(obj.__class__) # => '/usr/local/Cellar/python/2.7.11/Frameworks/Python.framework/Versions/2.7/lib/python2.7/decimal.pyc'
inspect.getfile(blah.__class__) # => '/usr/local/Cellar/python/2.7.11/Frameworks/Python.framework/Versions/2.7/lib/python2.7/decimal.pyc'
I would really love to understand why this is not working!
EDIT
It turns out that the issue only occurs when running under AppEngine's dev_appserver.py
environment. A simple:
isinstance(db.session.execute('SELECT amount FROM model LIMIT 1').fetchone()[0], decimal.Decimal)
returns False
when making a request via the AppEngine dev_appserver and True
when run from the console.
Ran into this today, and came across an old mailing list post that discussed this.
Turns out this question has also been addressed on stackoverflow previously as well. Pasting the answer here for ease of access / reduced stackoverflow server load:
It appears that the decimal.Decimal class is patched somewhere in the Google App Engine SDK (or the module is reloaded), and this is done between the MySQL conversion library importing decimal and you importing the same.
Luckily, we can work around this by updating MySQL conversion table:
from MySQLdb.constants import FIELD_TYPE
from MySQLdb.converters import conversions
import decimal
conversions[FIELD_TYPE.DECIMAL] = conversions[FIELD_TYPE.NEWDECIMAL] = decimal.Decimal
That's it; the above code resets the MySQL class and your JSON encoder type check succeeds.
The alternative would be for you to look for the class MySQLdb is using:
class DecimalEncoder(json.JSONEncoder):
def default(self, o):
if isinstance(o, MySQLdb.converters.conversions[MySQLdb.constants.FIELD_TYPE.DECIMAL]):
return float(o)
return super(DecimalEncoder, self).default(o)
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