Many-To-Many Relationship in ndb
Asked Answered
A

1

8

Trying to model a many-to-many relationship with ndb. Can anyone point to a good example of how to do this?

At here is an example of what I have at the moment:

class Person(ndb.Model):
     guilds = ndb.KeyProperty(kind="Guild", repeated=True)

class Guild(ndb.Model)
     members = ndb.KeyProperty(kind="Person", repeated=True)

     def add_person(self, person):
         self.members.append(person.key)
         self.put()
         person.guilds.append(self.key)
         person.put()

Is this the correct way to go about it? I have had a good look around but can't seem to find any good documentation on the subject.

In the datastore viewer, I can see this relationship being stored as a list of Keys, which I expect.

However, when I try to use them in the Person class methods like this:

for guild in self.guilds:

I get:

TypeError: 'KeyProperty' object is not iterable
Auroraauroral answered 24/6, 2014 at 16:55 Comment(2)
for guild in self.guilds should work. Check againt if you put repeated=True in Person: guilds = ndb.KeyProperty(kind="Guild", repeated=True)Saum
Note there is an upper limit to the number of relationships that can be achieved with repeated properties. If you need many thousands or more, many to many relationships, or you need to name you relationships then an intermediate entity that holds two keyproperties each pointing to each end of the relationship will also work, but it is generally less efficient and you may have to store some redundant info in this entity if you need summary views to avoid additional looklups.Reive
D
14

No. That is not the correct way to go about it.

You can model a many-to-many relationship with only one repeated property:

class Person(ndb.Model):
    guilds = ndb.KeyProperty(kind="Guild", repeated=True)

class Guild(ndb.Model):
    @property
    def members(self):
        return Person.query().filter(Person.guilds == self.key)

    def add_person(self, person):
        person.guilds.append(self.key)
        person.put()

or viceversa with:

class Person(ndb.Model):
    @property
    def guilds(self):
        return Guild.query().filter(Guild.members == self.key)

class Guild(ndb.Model):
    members = ndb.KeyProperty(kind="Person", repeated=True)

    def add_person(self, person):
        self.members.append(person.key)
        self.put()

The direction of the relationship depends on many factors (your business model, number of guilds per person, number of members per guild, etc.)

Dissension answered 24/6, 2014 at 21:51 Comment(4)
This worked great thanks. Still not sure why I was getting the: TypeError: 'KeyProperty' object is not iterable with the same type of property. This works for me but I think the upper limit on a repeated KeyProperty is 5000 so might not be as good for people with more entities.Auroraauroral
You're welcome. If you have a lot of entities on each side of the relationship maybe you should create another entity for the relationship, in the same way you create a new table in Relational DB for many-to-many relationships. But remember you can't do joins on datastore queries.Saum
That's something you should avoid. refer to this #15377619Leolaleoline
Thanks for your commment. You should avoid if at both sides you have a lot (>100) of values.Saum

© 2022 - 2024 — McMap. All rights reserved.