ComputedProperty only updates on second put ()
I have one ComputedProperty
inside StructuredProperty
that doesn't update when the object is first created.
When I create an object address_components_ascii
, it is not saved. The field does not appear at all in the data store viewer. But if I get()
, then immediately put()
again (without even changing anything), it ComputedProperty
works as expected. The field is address_components
working correctly.
I have tried clearing the database and deleting the entire database folder with no success.
I am using a local dev server on Windows 7. I have not tested it on GAE.
Here's the code:
class Item(ndb.Model):
location = ndb.StructuredProperty(Location)
Internal location class:
class Location(ndb.Model):
address_components = ndb.StringProperty(repeated=True) # array of names of parent areas, from smallest to largest
address_components_ascii = ndb.ComputedProperty(lambda self: [normalize(part) for part in self.address_components], repeated=True)
Normalization function
def normalize(s):
return unicodedata.normalize('NFKD', s.decode("utf-8").lower()).encode('ASCII', 'ignore')
Field example address_components
:
[u'114B', u'Drottninggatan', u'Norrmalm', u'Stockholm', u'Stockholm', u'Stockholms l\xe4n', u'Sverige']
and address_components_ascii
, after the second put()
:
[u'114b', u'drottninggatan', u'norrmalm', u'stockholm', u'stockholm', u'stockholms lan', u'sverige']
source to share
The real problem was the order that GAE calls _prepare_for_put()
in StructuredProperty
with respect to the _pre_put_hook()
surrounding call Model
.
I wrote address_components
to Item._pre_put_hook()
. I assume GAE figured ComputedProperty
StructuredProperty
out before calling _pre_put_hook()
on Item
. Reading from the ComputedProperty causes its value to be recalculated.
I added this to the end _pre_put_hook()
:
# quick-fix: ComputedProperty not getting generated properly
# read from all ComputedProperties, to compute them again before put
_ = self.address_components_ascii
I am storing the return value for a dummy variable to avoid IDE warnings.
source to share
I just tried this code on dev server and it worked. The computed property is available before and after put.
from google.appengine.ext import ndb
class TestLocation(ndb.Model):
address = ndb.StringProperty(repeated=True)
address_ascii = ndb.ComputedProperty(lambda self: [
part.lower() for part in self.address], repeated=True)
class TestItem(ndb.Model):
location = ndb.StructuredProperty(TestLocation)
item = TestItem(id='test', location=TestLocation(
address=['Drottninggatan', 'Norrmalm']))
assert item.location.address_ascii == ['drottninggatan', 'norrmalm']
item.put()
assert TestItem.get_by_id('test').location.address_ascii == [
'drottninggatan', 'norrmalm']
source to share
This appears to be a limitation in ndb
. Just executed put()
followed by get()
another put()
. This is slower, but only required when creating the object for the first time.
I added this method:
def double_put(self):
return self.put().get().put()
which is a replacement for put()
.
When I am a put()
new object, I call MyObject.double_put()
instead MyObject.put()
.
source to share