Monday, December 28, 2009

Google App Engine Datastore

I spent the better part of the past few days trying to wrap my head around the basics of the app engine datastore and the Python APIs written for it.  It is a bit of a different model than the relational one that I am used to.  More object based, no "table" design means you don't have to translate your objects into tables and then create the relationship tables that connect the various attributes.  This system is driven around your object model and makes a lot of sense.  I just had to drop my long history in relational design and move forward with an open mind.


So here is where you start, the Python Datastore API.  There is a lot of stuff there, I have only scratched the surface.  The GAE model is built on Bigtable, a technology I pointed out a few posts ago.  For a good high level explanation of how the Bigtable model is different from an RDBMS, check out this groovie post.  Like anything, the best way to learn this stuff is just to dive in and do it.  I added persistance using the GAE datastore to my running application.  This is the biggest piece so far.  Here is how I went about it.


Let me review the application I am writing.  A running training log application.  Very simple, login, add your daily run, list runs, logout.  That is it.  So to do this I created these models.


First a runner.  A runner is a subclass of type Model.  It lives in the google.appengine.ext.db package, which means it gets all the app engine persistance goodness included for free.  There are other types of database models to use, but this seemed the easiest for a first try.  My runner includes one property, the user as a UserProperty.  UserProperty is a really cool class that holds a Google user account, built in!


class Runner(db.Model):
    user = db.UserProperty()



I really didn't need a runner model.  You will see when I define a run below, I could have just as well put the runner in that model.  In fact, Bigtable sort of encourages you not to normalize the two classes the way I did.  Still, I kept it this way for a couple of reasons.  One, I wanted to learn how this parent/child grouping works (described below) and Bigtable will do some optimizations on parent/child grouping so this seemed like a good thing to me.  


Here is my run class, again a db Model.  It very simply holds the three properties I am interested in (name of run, distance, duration) and includes a time stamp that is auto created for us.


class Run(db.Model):
    name = db.StringProperty()
    distance = db.StringProperty()
    duration = db.StringProperty()
    date = db.DateTimeProperty(auto_now_add=True)


Simple!  Now the cool part, writing and reading these things.  I wrote a simple web page that provides a form for this data.  After logging in, the user can enter data in the form and submit it.  This is the class that saves the data after submission.


class RunLog(webapp.RequestHandler):
    def post(self):
        
        # Look for the current runner in the datastore first
        runner = Runner.all().filter('user =',users.get_current_user()).get()
        if not runner:
            runner = Runner()
            runner.user = users.get_current_user()
            runner.put()
         
        # Save the run when we have a runner parent
        run = Run(parent=runner)
        run.name = self.request.get('name')
        run.distance = self.request.get('distance')
        run.duration = self.request.get('duration')
        run.put()


        self.redirect('/listRuns')


Note a few things.  I first look to see if this runner has been here and saved runs before.  If so, I use that runner as the "parent" of the run.  This creates an Entity Group and according to what I read is intended to be used with data that is very transactional.  I can use transactions to update and manipulate all the data in this entity group, useful, but not so much for my app.  Again, I wanted to see how this parent/child thing works first, even if it is easier to just include the runner in the run class itself.    If I don't find a current "runner" I will create a new one and save the runs with that runner.


I did read one nice thing about Entity groups.  Apparently Bigtable with provide a bit of an optimization and keep all data related to an Entity group physically close together.  This should mean consistent response times for all data in your Entity group if your users are scattered across the globe.  Seemed cool to me.


Next, reading.  This actually comes first in the web design since the data is presented on the welcome page if you are logged in.  


class MainPage(webapp.RequestHandler):
    def get(self):
        nickname = None
        avatar = None
        run = None
        runner = None
        
        # Check for current user, populate data if found
        if users.get_current_user():
            runner = Runner.all().filter('user =',users.get_current_user()).get()
            if runner:
                run = Run.all().ancestor(runner)
            nickname = users.get_current_user().nickname()
            avatar = gravatar(users.get_current_user().email())
            
        template_values = {
            'runner': nickname,
            'avatar': avatar,
            'runs': run,
            }


        path = os.path.join(os.path.dirname(__file__), 'runs.html')
        self.response.out.write(template.render(path, template_values))


You can see that I am checking for a current user, if so, try to lookup to see if that user is a "runner" in our system.  If this person is a runner, get all of their runs using the "ancestor" call on the Query class that comes back from a call to Run.all().  This will filter on only runs that this runner has entered because we made the parent connection in the RunLog class.  Then I lookup a Gravatar if the user has one, and set the values for the template to render.


It took me a long time to get there, but that seems to me a basic setup.  I feel like I am at about step 1 understanding Bigtable and GAE persistence.  It is a new and different model, so any help is appreciated!

Tuesday, December 22, 2009

A few tweaks and a gravatar

Two quick problems I solved today that were a bit naging.

When running the app server in Eclipse, it complained I didn't have the Python Image Library (PIL) installed.  Not a huge deal, I'm not going to use PIL now anyway, but I hate runtime errors however innocent they may be.  So I downloaded the PIL source.  Unfortunately I didn't have gcc installed either, so off I go to to find out how to install gcc.  Found an essential developers toolkit for the Mac, Xcode.  This package is great, installs all the dev tools you know and love, and much more.  So with gcc now in place, I built and installed PIL using the instructions in the source.  Went well, and now I don't have an error message when I start the app server.

The second problem was the one I mentioned in the last post.  Everytime I ran the server from within Eclipse, I got this error message.

Variable references empty selection: ${project_loc}

Turns out you need to keep focus on the project as you run it.  So make sure the main project is selected in the package explorer before you execute the Run command.  I suppose this makes sure the ${project_loc} is set.  It sort of makes sense, but is still a little weak.  Thanks to juddsolutions blog post for pointing this out.  It wasn't a Pydev problem after all, we all had to deal with it.


Got a great piece of code from snipt, allowed me to add Gravatars in just about 5 minutes.  It was that easy.


Lastly, if anyone has used django_tables successfully with eclipse and pydev, please let me know.  I installed it correctly, but running the app server from within eclipse throws an error that the library is not found.  It is clearly in the path, both source and runtime (Run) path.  Don't know what is going on.

Sunday, December 20, 2009

Hello World App

Today I built the basic "Hello World" app as described on the code.google.com site.  It was pretty straight forward, but a few little things that I had to work through.  I wanted to use the Google App Engine plugin for Eclipse, but it is geared towards a Java dev environment, so don't bother installing this if you are using Python.  The Pydev plugin I described in the last post is all you need.

First thing to do is look at this article that describes how to work through the tutorial using Pydev.  The Google site is either generic or a bit Windows centric.  Read the article to see how Pydev works with this tutorial and GAE.  The only problem I had, and it is just my inexperience with the Mac, was getting the Mac file browser window to recognize any path.  When the browser came up, you get the very user centered view of the filesystem, but you cannot browse to things like /usr/local/google_appengine which is what I needed to do to add the source trees to the project.  After  few searches I found the best key sequence ever, command-shift-g, it brings up a text box for any file path!

Next, start down the hello world app path.  You start out with "Hello World", but quickly move on to a few more complex things.  An excellent tutorial!  You setup users, forms, and even persistent datastorage (through Bigtable).  I learned a ton.

The only problem I continue to have is a Pydev problem I think.  When I startup the app server from within Eclipse using the Python Run setup in the article, I get this error message about 90% of the time.

Variable references empty selection: ${project_loc}

Not sure why, ${project_loc} is just the location as defined by Eclipse, might be a Pydev problem.  I'll look on stackoverflow later to see what is going on.  Stackoverflow is a great site for programming questions.

Friday, December 18, 2009

Setting Up

First blog entry, what is this thing about? I want to write a web application that runs in the Google App Engine. The more I read about this app engine infrastructure, the more I wanted to dive in. This thing sounds amazing, a rich set of APIs, use of Google tools like gmail, wave, picasa, etc. Google hosts the application, scales for you, manages your deployments. You even get to use the datastore that other Google applications use, Bigtable (BTW, read this paper, they really took an interesting approach to datastore design). It really does sound like an amazing environment to build and host web apps. So let's find out. That is what this is about. I'm going to go through a bunch of code, examples, ups-and-downs, and document them here.

First things first. Like any good project, the best part is buying tools. I love browsing the Sears Craftsman section and buying a new tool. For this project, I need a new computer. My old Dell just isn't going to cut it. So on "Black Friday" I went down to the Apple store and bought a 21.5 inch iMac, entry level, no upgrades. It is beautiful. Unix system, great interface, and even came with the new wireless keyboard and magic mouse. Here it is:


Next up are books. Google App Engine supports two programming languages. The first language implemented was Python, because it is used a ton at Google and the inventor of the language works there, the BDFL. GAE now supports Java as well. Java would have been an easy choice for me, I have programmed in Java since it's alpha version. But I would rather go with the language GAE is based on, and I would love to learn Python. So I bought the nutshell book that Alex Martelli wrote. This is a great book, but it is a programming language spec book. I like it because it gives you every detail about the language, syntax, structure, implementation. A full specification of how Python works. Highly recommended.


Now to setup the system. This was super easy on the Mac, what a great system. Python 2.6.1 comes installed with OS X 10.6 and although Python 3 sounds very interesting and has a lot of great features, but I am going to stick with 2.6 because the app engine doesn't support 3.0 yet. I installed Eclipse and the Pydev Python language plugin. Pydev even supports code deployment to GAE within the plugin! Setup was a breeze, I was coding in Python in a matter of minutes following the Pydev install and setup instructions. By far the easiest language/IDE setup I have ever done. (not counting vi & c)


Ok, so there is the setup. Pretty standard and a lot of fun. Next up I will go through the GAE tutorial. Over the course of the next few weeks I hope to go through the tutorial, and then create a simple application. I'm thinking an app to track my running progress (i am something of a recreational runner), should be simple enough. And really the world needs yet another web site that allows you to track your morning run :-)


Yours sincerely, Arthur "two-sheds" Jackson