NDB Decimal Property

Go To StackoverFlow.com

0

I'm trying to migrate my application from the old App Engine db to ndb. Things were going well until I started getting weird errors associated with my custom Decimal property. In the old db, I used Nick Johnson's code (http://goo.gl/fpKNs). I made a few changes as far as the function names to be compatible with ndb, but the majority remains. Here's my modified version:

class DecimalProperty(db.Property):
    def _validate(self, value):
        if not isinstance(value, (Decimal, str)):
            raise datastore_errors.BadValueError('Expected decimal or string, got %r' % (value,))

        return Decimal(value)

    def _db_set_value(self, v, unused_p, value):
        if not isinstance(value, (str, Decimal)):
            raise TypeError('DecimalProperty %s can only be set to string values; received %r' % (self._name, value))
        v.set_stringvalue(str(value))

    def _db_get_value(self, v, unused_p):
        if not v.has_stringvalue():
            try:
                return Decimal(v)
            except ValueError:
                return None

        return Decimal(v.stringvalue())

And here's one example traceback:

Traceback (most recent call last):
  File "C:\Program Files (x86)\Google\google_appengine\lib\webapp2\webapp2.py", line 1536, in __call__
    rv = self.handle_exception(request, response, e)
  File "C:\Program Files (x86)\Google\google_appengine\lib\webapp2\webapp2.py", line 1530, in __call__
    rv = self.router.dispatch(request, response)
  File "C:\Program Files (x86)\Google\google_appengine\lib\webapp2\webapp2.py", line 1278, in default_dispatcher
    return route.handler_adapter(request, response)
  File "C:\Program Files (x86)\Google\google_appengine\lib\webapp2\webapp2.py", line 1102, in __call__
    return handler.dispatch()
  File "C:\Program Files (x86)\Google\google_appengine\lib\webapp2\webapp2.py", line 572, in dispatch
    return self.handle_exception(e, self.app.debug)
  File "C:\Program Files (x86)\Google\google_appengine\lib\webapp2\webapp2.py", line 570, in dispatch
    return method(*args, **kwargs)
  File "U:\Hefner\Dropbox\Public\Projects\GHI\dev\rpc.py", line 68, in get
    result = func(*args)
  File "U:\Hefner\Dropbox\Public\Projects\GHI\dev\rpc.py", line 278, in getDonations
    response = utilities.getAllDonations(self, settings_key, query_cursor)
  File "U:\Hefner\Dropbox\Public\Projects\GHI\dev\GlobalUtilities.py", line 361, in getAllDonations
    query = models.Donation.gql("WHERE settings_key = :s ORDER BY time_created DESC", s=settings_key)
  File "C:\Program Files (x86)\Google\google_appengine\google\appengine\ext\ndb\model.py", line 2885, in _gql
    *args, **kwds)
  File "C:\Program Files (x86)\Google\google_appengine\google\appengine\ext\ndb\query.py", line 1318, in gql
    qry = _gql(query_string)
  File "C:\Program Files (x86)\Google\google_appengine\google\appengine\ext\ndb\utils.py", line 136, in positional_wrapper
    return wrapped(*args, **kwds)
  File "C:\Program Files (x86)\Google\google_appengine\google\appengine\ext\ndb\query.py", line 1366, in _gql
    if prop._name != name:
AttributeError: 'str' object has no attribute '_name'

I may be wrong, but it seems like the Decimal Property is returning a string instead of an instance of the property and I have no idea what to do about that. When I comment out the amount_donated = DecimalProperty() in my model class, everything works perfectly.

2012-04-05 19:57
by rhefner1


2

Looks like you pasted in the ld db code...

Anyway, see these docs: https://developers.google.com/appengine/docs/python/ndb/subclassprop

You want to use _to_base_type() and _from_base_type().

Good luck!

2012-04-06 05:27
by Guido van Rossum
Thanks! Reading those docs made me realize that all I had to do was change class DecimalProperty(db.Property) to class DecimalProperty(db.TextProperty). This gave me access to the functions you mentioned - rhefner1 2012-04-06 15:34
I'm guessing you're using something like import google.appengine.ext.ndb as db? Please don't do that. It confuses everyone. NDB will never be renamed to db, even if we ever deprecate db - Guido van Rossum 2012-04-07 04:20


1

This is what I use for DecimalProperty, to store currency, for what it's worth.

class DecimalProperty(ndb.IntegerProperty):
    # Decimal property ideal to store currency values, such as $20.34 
    # See https://developers.google.com/appengine/docs/python/ndb/subclassprop
    def _validate(self, value):
        if not isinstance(value, (Decimal, str, unicode, int, long)):
            raise TypeError('Expected a Decimal, str, unicode, int or long an got instead %s' % repr(value))

    def _to_base_type(self, value):
        return int(Decimal(value) * 100)

    def _from_base_type(self, value):
        return Decimal(value)/Decimal(100) 
2013-01-15 08:41
by bustrofedon


1

The answer given by bustrofedon contains rounding issues and a bug. Here is a better implementation of a CurrencyProperty :

class CurrencyProperty(ndb.IntegerProperty):
    def _validate(self, value):
        if not isinstance(value, (Decimal, float, str, unicode, int, long)):
            raise TypeError("value can't be converted to a Decimal.")

    def _to_base_type(self, value):
        return int(round(Decimal(value) * 100))

    def _from_base_type(self, value):
        return Decimal(value) / 100
2014-03-09 17:00
by Dalmas
Ads