Richard Haber
Richard Haber

Reputation: 135

NDB Modeling One-to-one with KeyProperty

I'm quite new to ndb but I've already understood that I need to rewire a certain area in my brain to create models. I'm trying to create a simple model - just for the sake of understanding how to design an ndb database - with a one-to-one relationship: for instance, a user and his info. After searching around a lot - found documentation but it was hard to find different examples - and experimenting a bit (modeling and querying in a couple of different ways), this is the solution I found:

from google.appengine.ext import ndb

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

    @classmethod
    def get_by_name(cls, name):
        return cls.query(cls.name == name).get()

    def get_info(self):
        return Info.query(Info.monster == self.key).get()

class Info(ndb.Model):
    monster = ndb.KeyProperty(kind='Monster')
    address = ndb.StringProperty()

a = Monster(name = "Dracula")
a.put()

b = Info(monster = a.key, address = "Transilvania")
b.put()

print Monster.get_by_name("Dracula").get_info().address

NDB doesn't accept joins, so the "join" we want has to be emulated using class methods and properties. With the above system I can easily reach a property in the second database (Info) through a unique property in the first (in this case "name" - suppose there are no two monsters with the same name).

However, if I want to print a list with 100 monster names and respective addresses, the second database (Info) will be hit 100 times.

Question: is there a better way to model this to increase performance?

Upvotes: 0

Views: 1422

Answers (2)

Tim Hoffman
Tim Hoffman

Reputation: 12986

If its truly a one to one relationship, why are creating 2 models. Given your example the Address entity cannot be shared with any Monster so why not put the Address details in the monster.

There are some reasons why you wouldn't.

  1. Address could become large and therefore less efficient to retrieve 100's of properties when you only need a couple - though project queries may help there.

  2. You change your mind and you want to see all monsters that live in Transylvania - in which case you would create the address entity and the Monster would have the key property that points to the Address. This obviously fails when you work out that some monsters can live in multiple places (Werewolfs - London, Transylvania, New York ;-) , in which case you either have a repeating KeyProperty in the monstor or an intermediate entity that points to the monster and the address. In your case I don't think that monsters on the whole have that many documented Addresses ;-)

Also if you are uniquely identifying monsters by name you should consider storing the name as part of the key. Doing a Monster.get_by_id("dracula") is quicker than a query by name.

As I wrote (poorly) in the comment. If 1. above holds and it is a true one to one relationship. I would then create Address as a child entity (Monster is the parent/ancestor in the key) when creating address. This allows you to,

  1. allow other entities to point to the Address,
  2. If you create a bunch of child entities, fetch them with a single ancestor query). 3 If you have get monster and it's owned entities again it's an ancestor query.
  3. If you have a bunch of entities that should only exist if Monster instance exists and they are not children, then you have to do querys on all the entity types with KeyProperty's matching the key, and if theses entities are not PolyModels, then you have to perform a query for each entity type (and know you need to perform the query on a given entity, which involves a registry of some type, or hard coding things)

Upvotes: 1

user784435
user784435

Reputation:

I suspect what you may be trying could be achieved by using elements described in the link below Have a look at "Operations on Multiple Keys or Entities" "Expando Models" "Model Hooks"

https://developers.google.com/appengine/docs/python/ndb/entities

(This is probably more a comment than an answer)

Upvotes: 0

Related Questions