8
votes

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
1
for guild in self.guilds should work. Check againt if you put repeated=True in Person: guilds = ndb.KeyProperty(kind="Guild", repeated=True) - Hernán Acosta
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. - Tim Hoffman

1 Answers

14
votes

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.)