Reputation: 1
I'm quite new to ndb. This is how my structure looks like in general:
a = [b, c]
b = [d, e, f]
d = [g, h]
e = [k, l, m, n]
f = [o]
c = [p, r, t]
I have the following model.
class Child(ndb.Model):
name = ndb.StringProperty()
child = ndb.KeyProperty(kind="Child", repeated=True)
class Root(ndb.Model):
name = ndb.StringProperty()
child = db.StructuredProperty(Child, repeated=True)
I can't do this since ndb won't allow me to repeat it because I already repeat Child.
What would be the proper way to model this structure?
Upvotes: 0
Views: 196
Reputation: 71
Since the entities of the Root and Child kinds are almost the same, The data I see you are trying to model is a classic example of one-to-many relationship between entities of the same kind. The modelling for this sort of relationship is below:
class RelatedKind(ndb.Model):
name = ndb.StringProperty()
root = ndb.KeyProperty(kind="RelatedKind")
To create entities:
a = RelatedKind(name='a')
a_key = a.put()
b = RelatedKind(name='b', root=a_key)
b_key = b.put()
c = RelatedKind(name='c', root=a_key)
c_key = c.put()
# To get all 'child' of a;
child_a = RelatedKind.query(root == a_key).fetch()
print(child_a)
# >>> [b_key, c_key]
With datastore query, and just keyproperty, you achieve the same modelling without using repeated.
Upvotes: 3
Reputation: 13130
Keep in mind a few things. Suppose that you imagine records as being like files on your filesystem.
So, if you have the "root" object "contain" all the children via a repeated property, that'll result in you having a root file that can only be updated once every second or so, and it'll eventually grow too large.
So, in lieu of that, you have a few choices. You can use use ancestor queries, like Jeff mentioned. Or, you can just use all pointers and use a query to child any node's children:
class Node(ndb.Model):
parent = ndb.KeyProperty(kind='Node')
def get_children(self):
return Node.query().filter(Node.parent == self.key)
You can use get_children to fetch any node's children. Note that this part is eventually consistent, so recently added nodes won't necessarily show up in get_children for generally only a second or so.
root = Node(parent=None)
child1 = Node(parent=root)
child2 = Node(parent=root)
child3 = Node(parent=root)
sub_child1 = Node(parent=child1)
Upvotes: 0
Reputation: 138
I don't see why you need a KeyProperty
on the child. You could model your relationship like so:
class Child(ndb.Model):
name = ndb.StringProperty()
class Root(ndb.Model):
name = ndb.StringProperty()
child = ndb.KeyProperty(repeated=True)
c1 = Child(name="b").put()
c2 = Child(name="c").put()
a = Root(child=[c1,c2]).put() # put returns the key; otherwise you would need c1.key() here
children_keys = a.get().child # [Key(Child, 1234), Key(Child, 4567)]
# to retrieve the children, you could do
children = [ key.get() for key in children_keys ]
Upvotes: 0
Reputation: 17076
If you just want to be able to store many 'Child' entities on a single 'Root', you can use a LocalStructuredProperty
to contain the Child
model instead (but this means it won't be indexed). There's a hint to this behavior in the App Engine NDB docs when it discusses nested structured properties:
Although a StructuredProperty can be repeated and a StructuredProperty can contain another StructuredProperty, beware: if one structured property contains another, only one of them can be repeated. A work-around is to use LocalStructuredProperty, which does not have this constraint (but does not allow queries on its property values).
Another option for modeling nested relationships like this would be to use ancestors on the keys. So, for example, let's say your Root key path were: ('Root', 1)
. You could add children below it with keys ('Root', 1, 'Child', 1)
, ('Root', 1, 'Child', 5)
, and so on, appending 'Child' to the keypath each time. Then, we you wanted to query for the children of an object, you could just use an ancestor query, e.g.:
def create_child(parent, name):
new_child = Child(parent=parent.key, name=name)
new_child.put()
return new_child
def get_children(parent):
return Child.query(ancestor=parent.key)
class Child(ndb.Model):
name = ndb.StringProperty()
class Root(ndb.Model):
name = ndb.StringProperty()
You don't really even need to have a Root
anymore at this point, because you can assign any arbitrary keypath, and you could also use the name
as an ID instead and store less information.
That said, it's really completely dependent on what you're actually trying to model, there's not really enough information here to understand what you mean.
Upvotes: 2