1

I am using Endpoints-proto-datastore created by Danny Hermes for Google App Engine.

My model looks something like this:

class Datum(EndpointsModel):
    year = ndb.IntegerProperty(required=True)
    value = ndb.FloatProperty(required=True)

class Variable(EndpointsModel):
    name = ndb.StringProperty(required=True)
    data = ndb.StructuredProperty(Datum, repeated=True)

class Indicator(EndpointsModel):
    name = ndb.StringProperty(required=True)
    variables = ndb.KeyProperty(kind=Variable, repeated=True)
    formula = ndb.StringProperty(required=True)

And my API is something like this:

@endpoints.api(name="SomeAPI", version="v1", description="someDescription")
class SomeAPI(remote.Service):
    @Indicator.query_method(query_fields=("limit", "pageToken"), 
                            name="indicator.list",
                            path="indicators")
    def list_indicators(self, query):
        return query

The problem is that when i make the request, i get

{
 "items": [
  {
   "name": "IndicatorName",
   "variables": [
    "agtkZXZ-bW9uaXRvcnITCxIIVmFyaWFibGUiBU1BVFJQDA",
    "agtkZXZ-bW9uaXRvcnISCxIIVmFyaWFibGUiBFBST1AM"
   ],
   "formula": "someFormula"
  }
 ]
}

But getting the variable keys is not really useful for me, because that would force the client to make another request for the variables on the indicator entity. I would like to get the variable content, like this:

{
 "items": [
  {
   "name": "IndicatorName",
   "variables": [
     {
      "name": "some Variable",
      "data": [
       {
        "value": 230,
        "year": 2000,
       },
       {
        "value": 250,
        "year": 2005,
       }
      ]
     },
     {
      "name": "some other Variable",
      "data": [
       {
        "value": 230,
        "year": 2000,
       },
       {
        "value": 250,
        "year": 2005,
       },
       {
        "value": 260,
        "year": 2010,
       }
      ]
     }
   ],
   "formula": "someFormula"
  }
 ]
}

1 Answers1

2

You don't want a KeyProperty then. If you want to reference the other property use ndb.StructuredProperty:

class Indicator(EndpointsModel):
    name = ndb.StringProperty(required=True)
    variables = ndb.StructuredProperty(Variable, repeated=True)

In advanced cases like yours, where the referenced object may change, but the keys will not, you can use an EndpointsAliasProperty. For some point of reference, see the docs for some examples or some StackOverflow questions Using endpoints-proto-datastore, how do you pass attributes to a method that are not contained in the EndpointsModel or Cloud Endpoints - Retrieving a single entity from datastore (by a property other than the helper methods provided by EndpointsModel).

UPDATE: After more information about the classes was added, indicating a special need, I added the following:

For this specific case, you'd want to store the variables as some other name like variable_keys and then use variables to retrieve the values of the keys:

from endpoints_proto_datastore.ndb import EndpointsAliasProperty

class Indicator(EndpointsModel):
    name = ndb.StringProperty(required=True)
    variable_keys = ndb.KeyProperty(kind=Variable, repeated=True)
    formula = ndb.StringProperty(required=True)

and then as an instance method on Indicator define your getter (for variables) with no associated setter:

    @EndpointsAliasProperty(repeated=True, property_type=Variable.ProtoModel())
    def variables(self):
      return ndb.get_multi(self.variable_keys)

I would also advise setting _message_fields_schema on the Indicator class so this getter is only called when you want it, since the get_multi is an expensive RPC if you don't use it. Then when you want it in your query, you can include it in the collection_fields:

    @Indicator.query_method(query_fields=("limit", "pageToken"), 
                            collection_fields=("variables", ...),
                            name="indicator.list",
                            path="indicators")
    def list_indicators(self, query):
        return query

PS: Check out PEP8 - Whitespace in Expressions and Statements; "Don't use spaces around the = sign when used to indicate a keyword argument or a default parameter value."

Community
  • 1
  • 1
bossylobster
  • 9,993
  • 1
  • 42
  • 61
  • The reason i'm using a `KeyProperty` instead of a `StructuredProperty` is that Variable has a repeated `StructuredProperty`. I guess i could use a `LocalStructuredProperty` instead, but it doesn't allow queries on its property values. Thanks for the style comment, i didn't know that – chocobo_rider Aug 20 '13 at 18:06
  • The definition of `Variable` above does not. Care to add the actual definition? – bossylobster Aug 20 '13 at 19:27
  • i remembered another reason for not using `StructuredProperty`. In my application many Indicators can reference to the same variable. If i understand how `StructuredProperty` works, whenever i need to update the variable, i'll have to update it in every indicator the variable is in. If i use `KeyProperty` i only need to update the variable entity once. – chocobo_rider Aug 20 '13 at 19:41
  • I updated the model definition. The indicator data is calculated on the client side using the formula and the variables data. That's why multiple indicators can depend (at least partially) on the same variables, for example: GDP_per_capita = GDP/population public_spending_on_education = (spending_on_education/GDP)*100 Both indicators depend on GDP – chocobo_rider Aug 20 '13 at 22:23