Ani Ani - 3 months ago 7
Python Question

Why does my use of NDB's `populate()` not accept `id` or `parent`, but only `key`?

I want to create an entity object and after its construction, before writing it into the datastore, I want to set parent and id.

According to App Engine docs, the constructor accepts these keyword arguments:
-

id

-
key

-
parent



You cannot easily define a property named "key", "id", "parent", or
"namespace". If you pass, for example, key="foo" in a constructor or
populate() call, it sets the entity's key, not a property attribute
named "key".


For
populate()
, it says it would accept the same keyword arguments as the constructor. However, it seems I'm doing something wrong, because the only keyword argument that works is
key
. Using
id
and/or
parent
gives me errors.

class Foo(ndb.Model):
pass

foo = Foo()
foo.populate(key=ndb.Key('Bar', 1, 'Foo', 123))
foo.key == ndb.Key('Bar', 1, 'Foo', 123) # True


When instead I use the keyword
parent
...

f.populate(parent=ndb.Key('Bar', 1))


...I get this traceback:

Traceback (most recent call last):
File "<console>", line 1, in <module>
File "/Applications/GoogleAppEngineLauncher.app/Contents/Resources/GoogleAppEngine-default.bundle/Contents/Resources/google_appengine/google/appengine/ext/ndb/model.py", line 2960, in _populate
self._set_attributes(kwds)
File "/Applications/GoogleAppEngineLauncher.app/Contents/Resources/GoogleAppEngine-default.bundle/Contents/Resources/google_appengine/google/appengine/ext/ndb/model.py", line 2970, in _set_attributes
prop = getattr(cls, name) # Raises AttributeError for unknown properties.
AttributeError: type object 'Foo' has no attribute 'parent'


or sometimes this (not sure what makes the difference):

File "<console>", line 1, in <module>
File "/Applications/GoogleAppEngineLauncher.app/Contents/Resources/GoogleAppEngine-default.bundle/Contents/Resources/google_appengine/google/appengine/ext/ndb/model.py", line 2960, in _populate
self._set_attributes(kwds)
File "/Applications/GoogleAppEngineLauncher.app/Contents/Resources/GoogleAppEngine-default.bundle/Contents/Resources/google_appengine/google/appengine/ext/ndb/model.py", line 2972, in _set_attributes
raise TypeError('Cannot set non-property %s' % name)
TypeError: Cannot set non-property parent


If I use
id
...

f.populate(id=123)


I get again an attribute error:

Traceback (most recent call last):
File "<console>", line 1, in <module>
File "/Applications/GoogleAppEngineLauncher.app/Contents/Resources/GoogleAppEngine-default.bundle/Contents/Resources/google_appengine/google/appengine/ext/ndb/model.py", line 2960, in _populate
self._set_attributes(kwds)
File "/Applications/GoogleAppEngineLauncher.app/Contents/Resources/GoogleAppEngine-default.bundle/Contents/Resources/google_appengine/google/appengine/ext/ndb/model.py", line 2970, in _set_attributes
prop = getattr(cls, name) # Raises AttributeError for unknown properties.
AttributeError: type object 'Foo' has no attribute 'id'


Shouldn't all of my
populate()
examples above work with any of the keyword arguments?

I know, I could only use
key
to achieve the same as with
parent
and
id
together, but I would like to know what I'm missing here.

Answer

parent is a property of a Key when using ancestor paths. The constructor accepts it as a convenience but since it is not its own property, populate() will complain that it does not exist. Same goes for id. The constructor uses id to construct a Key using _get_kind() and the value of id.

An example is worth 1000 comments. See how id and parent are used to construct a key

>>> from google.appengine.ext import ndb
>>>>
>>> class Foo(ndb.Model):
...     pass
...
>>> foo = Foo(id=123, parent=ndb.Key('Bar', 1))
>>> foo.key
Key('Bar', 1, 'Foo', 123)
>>> foo.id
Traceback (most recent call last):
  File "<console>", line 1, in <module>
AttributeError: 'Foo' object has no attribute 'id'
>>> foo.parent
Traceback (most recent call last):
  File "<console>", line 1, in <module>
AttributeError: 'Foo' object has no attribute 'parent'