Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ComputedProperty only updates on second put()

I have a ComputedProperty inside a StructuredProperty that does not get updated when the object is first created.

When I create the object address_components_ascii does not get saved. The field is not visible in the Datastore Viewer at all. But if I get() and then immediately put() again (even without changing anything), the ComputedProperty works as expected. The address_components field works properly.

I have tried clearing the database, and deleting the whole database folder, without success.

I am using the 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)

The inner 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)

The normalization function

def normalize(s):
    return unicodedata.normalize('NFKD', s.decode("utf-8").lower()).encode('ASCII', 'ignore')

An example of the address_components field:

[u'114B', u'Drottninggatan', u'Norrmalm', u'Stockholm', u'Stockholm', u'Stockholms l\xe4n', u'Sverige']

and the address_components_ascii field, after the second put():

[u'114b', u'drottninggatan', u'norrmalm', u'stockholm', u'stockholm', u'stockholms lan', u'sverige']
like image 353
Filip Haglund Avatar asked Jun 04 '26 01:06

Filip Haglund


2 Answers

The real problem seemed to be the order that GAE calls _prepare_for_put() on the StructuredProperty relative to the call to _pre_put_hook() of the surrounding Model.

I was writing to address_components in the Item._pre_put_hook(). I assume GAE computed the ComputedProperty of the StructuredProperty before calling the _pre_put_hook() on Item. Reading from the ComputedProperty causes its value to be recalculated.

I added this to the end of the _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'm saving the return value to a dummy variable to avoid IDE warnings.

like image 192
Filip Haglund Avatar answered Jun 06 '26 13:06

Filip Haglund


I just tried this code on dev server and its worked. Computed property is accessible 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']
like image 36
Dmytro Sadovnychyi Avatar answered Jun 06 '26 13:06

Dmytro Sadovnychyi