NDB dictionary

214 views
Skip to first unread message

Ryan Ang

unread,
Jan 18, 2017, 10:39:56 PM1/18/17
to Google App Engine
I have this sample json of a list of "Fruit":
{
  "apple":{
    "color":red"
    "taste":"sweet"
  }
  "pineapple":{
    "color":yellow"
    "taste":"sour"
  }
}
I want to use a very object oriented approach to retrieve a property of my entity using a string key.
The requirements:
1) NDB must not be able to store any "Fruit" without the "color" or "taste" fields. (JsonProperty does not meet this requirement)
2) "apple" and "pineapple" are unique identifiers, used to retrieve it's corresponding fruit data, NDB must not be able to store multiple fruit objects containing the same unique identifier.

class Fruit(ndb.Model):
  name = ndb.StringProperty(required=True)
  color = ndb.StringProperty(required=True)
  taste = ndb.StringProperty(required=True)

class Person(ndb.Model):
  favoritefruits = ndb.StructuredProperty(required=True,repeated=true)

Then to find apple,
person = Person.get_by_id("Johnathan").favouritefruits
for fruit in person.favoritefruits:
  if fruit.name == "apple"
    # found apple!

The problem:
Multiple "Fruit" objects containing the same "name" property can exist in the "favoritefruits" property.

What solutions are there that will fulfil both requirements 1) and 2)?

Vitaly Bogomolov

unread,
Jan 20, 2017, 12:38:32 AM1/20/17
to Google App Engine

Hi Ryan Ang Wei En

class Fruit(ndb.Model):
  name = ndb.StringProperty(required=True)
  color = ndb.StringProperty(required=True)
  taste = ndb.StringProperty(required=True)

Dont use a 'name' field for ID. There is a 'id' keyword for this purpose.

...

class Fruit(ndb.Model):
    color = ndb.StringProperty()
    taste = ndb.StringProperty()

...

apple1 = Fruit(id="apple", color='1', taste='1')
apple1.put()
apple2 = Fruit(id="apple", color='2', taste='2')
apple2.put()

# restore apple1 from ndb after saving apple2
apple1 = apple1.key.get()
self.assertEqual(apple1.color, apple2.color)

# one item into ndb
apple_count = len(Fruit.query().fetch(100))
self.assertEqual(apple_count, 1)


Richard Cheesmar

unread,
Jan 20, 2017, 11:53:43 AM1/20/17
to Google App Engine
use a pre put hook to do your checking:


class Fruit(ndb.Model):
   
name = ndb.StringProperty(required=True)
         color = ndb.StringProperty(required=True)
         taste = ndb.StringProperty(required=True)



   
def _pre_put_hook(self):

Ryan Ang

unread,
Jan 21, 2017, 6:18:19 PM1/21/17
to Google App Engine
Dear Anons,

First of all thank you for the replies, your care is greatly, greatly appreciated!
# one item into ndb
apple_count = len(Fruit.query().fetch(100))
self.assertEqual(apple_count, 1)
 
After reading this, I've realised that fruits analogy is a wrong example of the actual issue I am facing.
I would like to kindly request for some time to read this post.

I'm working on a server project for a petrol station company.
I have this station class.

class Station(modelpy.Model):
    shifts = ndb.JsonProperty()

The Station's key's id is the name of a station.
Here are the names of the actual stations: "West Coast", "Woodlands", "South View".
I use these station names in get_by_id() to get the station entity.
As you can see, Station consists of a JsonProperty called "shifts".
The "shifts" consists of the data as follows (shown in a json format):
{
  "South View": {
    "shifts": {
      "Full Day": {
        "start": "7am",
        "end": "7pm"
      }
    }
  },
  "West Coast": {
    "shifts": {
      "Morning Shift": {
        "start": "10am",
        "end": "10pm"
      },
      "Night Shift": {
        "start": "10pm",
        "end": "10am"
      }
    }
  },
  "Woodlands": {
    "shifts": {
      "Morning Shift": {
        "start": "8am",
        "end": "8pm"
      },
      "Night Shift": {
        "start": "8pm",
        "end": "8am"
      }
    }
  }
}
I need a solution for these 3:
1) Ndb must not be allowed to store more than 1 shift (e.g. key.put() replaces any (if existing) entity with equal id).
2) Ndb must contain the "start" and "end" required property for every shift (e.g. required=True).
3) Most IMPORTANT: A MAINTAINTABLE solution that does not need to be re-implemented for other similar examples. (e.g. NOT write custom assertion every single time!)

JsonProperty demands a crap-ton of maintainability
I don't want to write a custom assertion every time, instead I want a generic solution (dictionary).

Don't mind the caps, I'm not shouting at anyone, just emphasis.

Love,
Ryan

Vitaly Bogomolov

unread,
Jan 22, 2017, 4:24:48 AM1/22/17
to Google App Engine
Hi, Ryan.
Maybe, something like this?

class Shifts(modelpy.Model):
    start = ndb.StringProperty(default='')
    end = ndb.StringProperty(default='')


def convert_json_to_ndb():
    # assume, that Station contain small amount of records
    for station in Station.query():
        for key, props in station.shifts.items():

            Shifts(
              id=key,
              parent=station.key,
              start=props['start'],
              end=props['end'],
            ).put()


Then, access to shifts:

west_coast_shifts = Station.query(ancestor=ndb.Key(Station, 'West Coast'))

for item in west_coast_shifts:
    print "name", item.key.id(), "start", item.start, "end", item.end


WBR, Vitaly

Vitaly Bogomolov

unread,
Jan 22, 2017, 4:26:52 AM1/22/17
to Google App Engine
Then, access to shifts:

west_coast_shifts = Station.query(ancestor=ndb.Key(Station, 'West Coast'))

oops

west_coast_shifts = Shifts.query(ancestor=ndb.Key(Station, 'West Coast'))

Ryan Ang

unread,
Jan 23, 2017, 6:32:21 AM1/23/17
to Google App Engine
Thank you for the help, I am learning a lot today!

If I delete the entity 'West Coast' station entity, will all existing shifts with "ancestor == ndb.Key(Station, 'West Coast')" automatically get deleted?

I want to prevent data store leaks.

Vitaly Bogomolov

unread,
Jan 23, 2017, 1:07:32 PM1/23/17
to Google App Engine
 
If I delete the entity 'West Coast' station entity, will all existing shifts with "ancestor == ndb.Key(Station, 'West Coast')" automatically get deleted?


GAE Datastore don't have any automatic triggers.

You can create your own triggers and remove redundant records. Google for "GAE _pre_delete_hook _post_delete_hook"

WBR, Vitaly
Reply all
Reply to author
Forward
0 new messages