user714852
user714852

Reputation: 2154

NDB model's custom property not returning correct value when deployed

I have declared 2 ndb models as follows:

class Event(ndb.Model):
    event_type = ndb.KeyProperty()

    @property
    def installments(self):
        return EventInstallment.query().filter(EventInstallment.event == self.key).count()

class EventInstallment(ndb.Model):
    event = ndb.KeyProperty()

I persist a single entity of type Event and another of type EventInstallment. EventInstallment.event is the Key of the declared Event entity. The following query works (i.e. returns 1) when run locally but not when deployed:

event_query = Event.query()
event_query = event_query.filter(ndb.GenericProperty('installments') > 0)
print event_query.count()

I have cleared memcache, and double checked that all properties of EventInstallment are correct. When opening the EventInstallment entity in datastoreviewer it has a hotlink to the Event key, as expected.

Can someone tell me what's going wrong here? Specifically I'm curious to know why this works locally and not when deployed.

Upvotes: 0

Views: 151

Answers (1)

Alex Martelli
Alex Martelli

Reputation: 881555

The filter must be able to run in the datastore, and, in a deployed app, filter(ndb.GenericProperty('installments') > 0) clearly can't (the simulation dev_appserver does is not 100% accurate, I bet its authors never dreamed of checking for something like that).

Rather, what you want is a ComputedProperty, which is computed and actually sent to the datastore at put time, allowing the datastore to perform searches on it. To wit:

class Event(ndb.Model):
    event_type = ndb.KeyProperty()

    @property
    def _installments(self):
        return EventInstallment.query().filter(EventInstallment.event == self.key).count()

    installments = ndb.ComputedProperty(lambda self: self._installments)

Now, filter(Event.installments > 0) will work -- as long as the number of related EventInstallment entities at the time the Event entity was last put was positive. (The datastore won't re-run any code to update Event entities when EventInstallment entities are added, removed, or altered; ComputedProperty is computed at put time on the entity it belongs to, and it's computed on the appengine instance side of things, i.e in "application level code", just before the entity's sent to the datastore side of things).

You may want to work the other way 'round: query for EventInstallment entities, making a set of their event properties to remove duplicates, and using the len of that set as your desired count. Yes, that requires fetching all of them, but counting is not much faster than fetching anyway.

Upvotes: 2

Related Questions