Jonny
Jonny

Reputation: 16298

GAE inheritance in datastore

I am trying to create something like a folder structure for saving to GAE ndb datastore. I will be saving a top root folder (MjlistitemFolder) to the datastore. A MjlistitemFolder can have a number of other subitems (Mjlistitem) in its items property. Ultimately the content of a folder items will be one of these: MjlistitemFolder, MjlistitemJobGeneric, MjlistitemJobApp

This all works if I create this structure in memory. But after put()ing it away and reloading the root folder from the datastore, I don't get the same structure back.

class Mjlistitem(ndb.Model):
    pass

class MjlistitemFolder(Mjlistitem):
    title = ndb.StringProperty()
    items = ndb.StructuredProperty(Mjlistitem, repeated=True)

class MjlistitemJob(Mjlistitem):
    pass

class MjlistitemJobGeneric(MjlistitemJob):
    jobtype = ndb.IntegerProperty()

class MjlistitemJobApp(MjlistitemJob):
    appleid = ndb.StringProperty()

I get these warnings:

WARNING  2014-04-04 07:14:17,506 model.py:2359] Skipping unknown structured subproperty (items.items.appleid) in repeated structured property (items of MjlistitemFolder)
WARNING  2014-04-04 07:14:17,506 model.py:2359] Skipping unknown structured subproperty (items.items.jobtype) in repeated structured property (items of MjlistitemFolder)
WARNING  2014-04-04 07:14:17,506 model.py:2359] Skipping unknown structured subproperty (items.items.appleid) in repeated structured property (items of MjlistitemFolder)

It seems like the db→instance process renders the stuff in items to be of Mjlistitem class only. How do I make them appear as their real inherited classes?

This is how I create a test structure:

        rootfolder = MjlistitemFolder(title="root")

        subfolder = MjlistitemFolder(title="Cool things")
        subfolder.items.append(MjlistitemJobApp(appleid="281796108")) # evernote
        subfolder.items.append(MjlistitemJobGeneric(jobtype=3)) # phone number
        subfolder.items.append(MjlistitemJobApp(appleid="327630330")) # dropbox

        rootfolder.items.append(MjlistitemJobGeneric(jobtype=15)) # passbook
        rootfolder.items.append(subfolder)
        rootfolder.items.append(MjlistitemJobGeneric(jobtype=17)) # appstore
        rootfolder.put()

Upvotes: 1

Views: 183

Answers (1)

lucemia
lucemia

Reputation: 6617

use Polymodel with repeated KeyProperty

The StructuredProperty need to be changed to KeyProperty because:

TypeError: This StructuredProperty cannot use repeated=True because its model class (Mjlistitem) contains repeated properties (directly or indirectly).

The Model

from google.appengine.ext import ndb
from google.appengine.ext.ndb import polymodel

class Mjlistitem(polymodel.PolyModel):
    pass

class MjlistitemFolder(Mjlistitem):
    title = ndb.StringProperty()
    # StructuredProperty won't allow you to apply on nested model, use key property instead
    items = ndb.KeyProperty(kind=Mjlistitem, repeated=True)

class MjlistitemJob(Mjlistitem):
    pass

class MjlistitemJobGeneric(MjlistitemJob):
    jobtype = ndb.IntegerProperty()

class MjlistitemJobApp(MjlistitemJob):
    appleid = ndb.StringProperty()

The Usage

 def test():
    rootfolder = MjlistitemFolder(title="root")

    subfolder = MjlistitemFolder(title="Cool things")
    subfolder.items.append(MjlistitemJobApp(appleid="281796108").put()) # evernote
    subfolder.items.append(MjlistitemJobGeneric(jobtype=3).put()) # phone number
    subfolder.items.append(MjlistitemJobApp(appleid="327630330").put()) # dropbox

    rootfolder.items.append(MjlistitemJobGeneric(jobtype=15).put()) # passbook
    rootfolder.items.append(subfolder.put())
    rootfolder.items.append(MjlistitemJobGeneric(jobtype=17).put()) # appstore
    rootfolder.put()

another thing should watch out.

the repeated property won't perform well if there are too many items in one folder, so it would be better if

class Mjlistitem(polymodel.PolyModel):
    parent = ndb.KeyProperty(kind="Mjlistitem")

    def set_parent(self, parent):
        assert isinstance(parent, MjlistitemFolder)
        self.parent = parent.key

class MjlistitemFolder(Mjlistitem):
    title = ndb.StringProperty()

class MjlistitemJob(Mjlistitem):
    pass

class MjlistitemJobGeneric(MjlistitemJob):
    jobtype = ndb.IntegerProperty()

class MjlistitemJobApp(MjlistitemJob):
    appleid = ndb.StringProperty()

The Usage

def test():
    rootfolder = MjlistitemFolder(title="root")
    rootfolder.put()

    subfolder = MjlistitemFolder(title="Cool things")
    subfolder.set_parent(rootfolder)
    subfolder.put()

    a = MjlistitemJobApp(appleid="281796108")
    a.set_parent(subfolder)
    a.put()

    b = MjlistitemJobGeneric(jobtype=3)
    b.set_parent(subfolder)
    b.put()
    c = MjlistitemJobApp(appleid="327630330")
    c.set_parent(subfolder)
    c.put()

Upvotes: 1

Related Questions