Can inheritance be modelled in app engine datastore by same kind and different properties?

380 Views Asked by At

Consider the following models and their properties:

Media
  ├── Image
  │       - CreatedAt
  │       - Width
  │       - Height
  ├── Audio
  │       - CreatedAt
  │       - Duration
  └── Video
          - CreatedAt
          - Width
          - Height
          - Duration

There are a few ways of implementing this:

1. Different kinds, different properties

This can be done by PolyModel in ndb. We have a base model Media which factors the common properties, and Image, Audio and Video can be subclasses.

Since each subclass has its own kind, querying the N newest media is not possible without in-memory sorting.

2. Same kind, same properties

Implementing this in datastore using single table inheritance is possible, but there is no point in enforcing 'unused' properties in a schemaless database. For example, there will be many rows for Audio entities with unused Width and Height properties.

Querying the N newest media is possible here, but it has the disadvantage of unused properties.

3. Same kind, different properties

Unlike relational databases, datastore does not require entities of the same kind to have the same properties. It is possible that Image, Audio and Video all to have the same Media kind, while having their own set of properties. An extra property called Type is necessary to distinguish them from each other.

Querying the N newest media is possible with this method, and there is no unused properties. But are there any gotchas with this approach? Are we losing any application level schema safety and data integrity with this?

2

There are 2 best solutions below

3
On

This is exactly what ndb's polymodel was designed for. However, it works differently than you described. Consider the following definitions:

class Media(polymodel.PolyModel):
  created_at = ndb.DateTimeProperty()
  ...

class Image(Media):
  height = ndb.IntegerProperty()
  width = ndb.IntegerProperty()

class Audio(Media):
  duration = ndb.FloatProperty()

Inside Datastore, an Image would be stored with the kind Media and with a property named class equal to ["Media", "Image"].

Using the models, you can query for any Media using:

Media.query()

But you can also query for individual types:

Image.query()

Note that the above query gets converted by ndb into the query:

Media.query().filter(Media.class == "Image")
0
On

You can also use an Expando class with dynamic properties, or just put all the attributes into a JsonProperty.