Google App Engine - Part 2: A Thousand Ways to Skin a Python
So, the Google App Server data model. Simply put, it works like a book does. A book that you can also plug into your computer with USB. Or connect your phone to with Bluetooth. And of course, it has a jet engine attached to it. With snakes slithering on it too.
Make sense? No? Good.
So, here's how it works. When defining a type of object which should be stored, it needs to extend the google.appengine.ext.db.Model class. Then, its attributes need to be one of the "Properties" defined in google.appengine.ext.db. For example:
class Map(db.Model): title = db.StringProperty() svg = db.BlobProperty()
A list of the types allowed can be found here. Simple enough so far. Making one of the Map objects and storing it in the database is even simpler:
class MapUpload(webapp.RequestHandler): def post(self): img = self.request.get('map') filename = self.request.POST['map'].filename map = Map() map.svg = db.Blob(img) map.title = filename map.put() self.redirect('/')
There is some weird dark magic that makes that actually work, but I'm trying not to concern my little head with it right now (Google is our benevolent overlord and we shouldn't question it... or something like that). Now, as far as access works, that's where the weirdness comes in. I will give you three ways to get exactly the same result.
# Way 1: Queries map_query = Map.all().filter('title =', 'mymap.svg') map = map_query.fetch(1)[0] # Way 2: GQL Query gql_query = db.GqlQuery("SELECT * FROM Map WHERE title = :1", 'mymap.svg') map = gql_query.get() # Way 3: DB ID (if you knew the ID beforehand) mapkey = self.request.GET['q'] map = db.get(db.Key(mapkey))
I don't know if anyone out there agrees with me, but all of these are equally mind-blowing... in the bad sense of the word. Also, they don't do Python or the "Pythonic" way to do stuff any justice. That's why I was rambling in the comments to my last post about object-oriented databases. However, Google seems to have foreseen people like me would not like any of the above, so they added this way of doing it:
class Game(db.Model): title = db.StringProperty() map = db.ReferenceProperty() # Later... map = Map() map.svg = db.Blob(img) map.title = filename map.put() game = Game() game.title = 'just a test title' game.map = map game.put()
Hey, hey. All of a sudden we see object references that are being stored! This is looking good! And, I can even do this:
game.map.title = 'some new title'
Much more Pythonic. Awesome.
Before I figured this out, one of my friends suggested I learn to live with the relational-ish database model, and just to juggling of key ID's and other junk. But frankly, I think the above is much cleaner and neater than a couple of verbose, ugly, and possibly insertion-vulnerable GQL queries.
Google seems to be smart enough to accommodate both guys like him and weirdos like me.
In other news, jwalsh, eternal vanquisher and hacker of my blog (yeah. right.), decided to join the blogosphere himself. Quick, everyone go spam comments on his blog!