Question

How can I set a parent/ancestor for an EndpointsModel and have the entity ID/Key generated automatically by datastore?

I've tried to adapt the keys_with_ancestors sample but seem to be hitting a bit of a block because it requires both id and parent to be specified. I'd like to do something similar except provide only parent id or key and have entity id/key auto-generated by the app engine datastore.

This following shows how I would do it using just NDB.

class Parent(ndb.Model):
    name = ndb.StringProperty()

class MyModel(ndb.Model):
    attr1 = ndb.StringProperty()
    attr2 = ndb.StringProperty()

p = Parent(name="Jerry")
p_key = p.put()  # or retrieve the key from somewhere else

mod = MyModel(parent=p_key)
mod.put()

Is this possible and could someone point me in the right direction? Thanks.

Was it helpful?

Solution

Following the keys_with_ancestors sample, let's assume we have the same imports and have defined the class MyParent in the same way it is defined there.

The TL;DR answer is essentially that passing parent= to the model constructor is equivalent to creating a key with None as the last ID in the list of kind, ID pairs. For example, for a class MyModel:

>>> parent = ndb.Key(MyModel, 1)
>>> child = MyModel(parent=parent)
>>> print child.key
ndb.Key('MyModel', 1, 'MyModel', None)

In order to do this with the sample, we could simply ignore the id:

class MyModel(EndpointsModel):
  _parent = None

  attr1 = ndb.StringProperty()
  attr2 = ndb.StringProperty()
  created = ndb.DateTimeProperty(auto_now_add=True)

and in the setter simply set the half-baked key and don't try to retrieve from the datastore (since the key is not complete):

  def ParentSet(self, value):
    if not isinstance(value, basestring):
      raise TypeError('Parent name must be a string.')

    self._parent = value
    if ndb.Key(MyParent, value).get() is None:
      raise endpoints.NotFoundException('Parent %s does not exist.' % value)
    self.key = ndb.Key(MyParent, self._parent, MyModel, None)

    self._endpoints_query_info.ancestor = ndb.Key(MyParent, value)

Similarly, in the getter, you can just retrieve the parent directly from the key (though this doesn't guarantee there is only a single pair as the parent):

  @EndpointsAliasProperty(setter=ParentSet, required=True)
  def parent(self):
    if self._parent is None and self.key is not None:
      self._parent = self.key.parent().string_id()
    return self._parent

Having done this you won't need to change any of the API code and the example will work as expected.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top