Continuing on my example with repos, commits and files, there are several ways to model entity relationships in Google Cloud Datastore.

Ancestor path

Entity models don't have relationship attributes and joined at runtime through parent attribute. Entities form entity group. This model provides high consistency but very low write throughput (1 write / second).

datastore-1

class Repo(ndb.Model):  
  pass

class Commit(ndb.Model):  
  sha = ndb.StringProperty()
  committer = ndb.StringProperty()
  message = ndb.StringProperty()

Here Commit is attached to Repo

 my_repo = models.Repo(id='my_repo')
    my_repo.put()
    models.Commit(
            sha='7c087b5',
            committer='Washington Irving',
            message='Initial commit',
            parent=my_repo.key
          ).put() 

This can be retrieved with ancestor query:

ancestor_key = ndb.Key('Repo', 'my_repo')  
commits = models.Commit.query(ancestor=ancestor_key).fetch()  

KeyProperty

KeyProperty resembles foreign key in relationship database. In contrast to ancestor path / parent it provides low consistency but high write throughput.

datastore-2

class Repo(ndb.Model):  
  pass

class Commit(ndb.Model):  
  repo = ndb.KeyProperty(kind=Repo)
  sha = ndb.StringProperty()
  committer = ndb.StringProperty()
  message = ndb.StringProperty()

In this design to retrieve Commits we don't need ancestor path:

repo_key = ndb.Key('Repo', 'my_repo')  
commits = models_v3.Commit.query(models_v3.Commit.repo == repo_key).fetch()  

StructuredProperty

Finally there's StructuredProperty to embed one entity to another. This can work in simple cases because Datastore could not provide indexing and retrieval of nested entities. It is also limited to just two levels.

datastore-3

class Commit(ndb.Model):  
  sha = ndb.StringProperty()
  committer = ndb.StringProperty()
  message = ndb.StringProperty()

class Repo(ndb.Model):  
  commits = ndb.StructuredProperty(
      Commit, repeated=True)

Code sample is here as well as NDB reference that I use all the time.