Lennart Paasse
Lennart Paasse

Reputation: 23

Delete hooks in ndb PolyModel childclass won't execute

I'm using ndb.polymodel.PolyModel to model different types of social media accounts. After an account has been deleted some cleanup has to be done (depending on the subclass).

I tried implementing a _pre_delete_hook on the subclass, but it never gets executed. When adding the exact same hook to its parent class it does get executed:

class Account(polymodel.PolyModel):
    user = ndb.KeyProperty(kind=User) 

    @classmethod
    def _pre_delete_hook(cls, key):
        # Works as expected
        pass

class TwitterAccount(Account):
    twitter_profile = ndb.StringProperty()

    @classmethod
    def _pre_delete_hook(cls, key):
        # Never gets called
        pass

t_account = TwitterAccount.get_by_id(1)
t_account.key.delete()

Seems strange to me as I would expect the subclass hook to override its parent. Or is this expected behavior?


Solution: I missed the fact that a delete operation happens on a Key (not a model instance). The key itself only knows the name of the topmost class (Account in this case).

I ended up defining a custom _delete_hook instance method on the subclass. When _pre_delete_hook gets called, I fetch the entity and then check if its class has a specific delete_hook to execute:

    # In subclass:
    def _delete_hook(self):
        # Do stuff specific for this subclass
        return

    # In parent class
    @classmethod
    def _pre_delete_hook(cls, key):
        s = key.get()
        if hasattr(s, '_delete_hook'):
            s._delete_hook()

Upvotes: 0

Views: 336

Answers (1)

Tim Hoffman
Tim Hoffman

Reputation: 12986

Unfortunately this is expected though non-obvious behaviour

When you call key delete with a PolyModel you are only calling delete on the parent class.

Have a look at the Key you are calling delete on, you will see the Kind is the parent Model. It then looks up the class via the Kind -> class map which will give you an Account class. Have a read up on how PolyModel works. It stores all Account sub classes as Account and has an extra property that describes the inheritance heirarchy. So that a query on Account will return all Account subclasses, but a query on TwitterAccount will only return that subclass.

Calling ndb.delete_multi won't work either.

If you want a specific PolyModel subclass pre delete hook to run, you will have to add a delete method to the subclass, call that, that can then call the subclass _pre_delete_hook (and may be call super).

But that will cause issues if you ever call key.delete directly

Upvotes: 0

Related Questions