Files

380 lines
25 KiB
Plaintext
Raw Permalink Normal View History

Episode: 252
Title: HPR0252: Google App Engine 101
Source: https://hub.hackerpublicradio.org/ccdn.php?filename=/eps/hpr0252/hpr0252.mp3
Transcribed: 2025-10-07 14:52:44
---
...
Yeah.
The following presentation from the Utah Open Source Conference held August 28th through
30th 2008 is underwritten by Ex-Mission.
Since 1993, Ex-Mission has developed and supported effective connectivity, hosting and co-location
services for thousands of Utah residents and businesses, Ex-Mission.com.
Streaming and podcast hosting bandwidth for this and many other presentations at podcast.utos.org
has been provided by Tier 4.
The presentation entitled Google, App, and Gen 101 was presented by Jonathan Ellis.
So I'm going to talk about using Google App Engine, specifically using Google App Engine
with Django.
As I was telling the gentleman here before we got started, Google officially says you
can use any Python web framework you like that supports the whiskey protocol.
In practice, they ship Django with the SDK, so they're really kind of saying, you know,
use Django.
And also, if you try to use other ones, Google kind of ripped the hell out of the standard
library, the Python standard library, and they only really bothered checking to see that
the library that Django needs are still there.
So a lot of the other frameworks are like, hello, we need the AST module and a bunch of other
things.
So in practice, you can get other things to run on it, but it's a lot of work.
And by a lot of work, I mean probably several hours.
So it's not what you want as your first introduction to App Engine.
Welcome, everyone, who's just coming in.
So I have my slides up here and some code that we're going to use for doing some hands-on
exercises here.
And over there on your right, the URL is on the right board.
Just go to a slash JLs and you'll get a directory listing and you can pick up those files
because we're not going to have this slide up very long.
So I'm going to talk about, and we're going to do a, I'm going to demonstrate a simple
two-dualist application.
And we're going to talk about the data model.
We're going to talk about controllers and views, which, which Django calls views and templates
respectively, which is a little confusing.
And then we're going to talk about URL mapping really briefly, which is, which is simple,
how Django connects the URLs to the controllers.
So that's what we're going to talk about.
I said Google Avenue ships Django, which, you know, it's true, but it's not useful because
they ship the 0961 version of Django, which is, you know, to be fair to Google, they didn't
have much choice.
It's the only stable release, but it's over a year old.
So, you know, the documentation doesn't reflect it anymore, and it's kind of old and busted
and still works, but it's not the new hotness anymore.
So the Mono is in release mode, beta 1 came out a couple weeks ago, beta 2 came out more
recently, and the Mono final is scheduled for early September.
So the code I'm demonstrating is using the beta 1.
And that's the way to go moving forward.
So what you need to do is use something called App Engine Helper.
And what that does is it allows you to download a newer version of Django and wedge it into
App Engine and just kind of remove the pull out the old Django that it ships with and
use the newer one.
And it does all the work.
It's like magic.
It's really cool.
There's nothing to know about Google App Engine.
App Engine and Django is, there's no relational database on App Engine.
So who's kind of familiar with App Engine, who, okay, so maybe we should back up a second.
App Engine is, it's kind of like a shared hosting service for Python code that doesn't have
a relational database.
What they have is they have the data store API, and what that is, it's a wrapper around
Google's big table, which you've probably heard of.
So what it is, it's basically an object database.
And you can retrieve objects by keys and you can query objects by their attributes.
It automatically indexes attributes for you.
So that's nice.
There are some limitations there that we'll talk about later.
The other things about Django are, it has a bunch of kind of helper libraries out of
the box.
And a lot of them don't work on Google App Engine like the section middleware and the
cache middleware.
You know, these stuff, this stuff, the App Engine helper fixes, it fixes the section
to work with the data store API, and Google recently released a MIM cache API.
So it can use that too.
But you know, it's not what, it can't do anything about the database API, it's never going
to be a relational database.
So Google App Engine and Django are both well documented.
The documentation for App Engine is here, documentation for Django is here.
So I'm just going to give kind of an overview of using them together because that's what's
missing.
There isn't any real, there aren't any sample applications provided using Django on App Engine.
And there isn't any documentation either on how to use App Engine helper here to make
your life easier.
There is this thing called, they have a page called running Django on Google App Engine,
which is basically how to write your own App Engine helper in five hours or less, you
know, it's not where you want to go.
So just use App Engine helper and your life will be good.
So here's how you get started, and you don't need to do this with the tarball because I've
already done it for you, but if you want to start your own application, these are the
steps you follow.
First of all, you need to get the App Engine helper from this URL and you need to get it
from SVN because the released version, which is released 52, doesn't support Django
1.0.
So you'll need to grab that from the subversion until they do a new release, which will
probably after Django 1.0 final.
So then you grab a Django tarball, and you unzip it, and then what you do is you take the
Django directly out and shove it in there, then you remove some files that you don't need
because Google has a 1,000 file limit, Django has more than that.
So you actually need to rip out the stuff that you won't be using.
That will allow it to work.
So there's two settings files that we're going to care about with Google App Engine.
One is the app.yaml, and this is really simple.
It's App Engine specific, and what we do, this is the only thing that I've changed from
what App Engine helper comes with.
App Engine helper comes with one of these out of the box.
It also comes with a settings.py out of the box.
And it's very same default.
I only had to change this and one line in settings.py, so I just gave it the name of what my application
is going to be.
And then this, I didn't have to change, but I put that in bold to emphasize it, because
the static DIR tells App Engine, this is where files live like, you know, your PNGs, your
JavaScript, your CSS, but don't need to be interpreted by App Engine.
And then everything else we're going to send to the Django main.py, which again, you don't
have to worry about App Engine helper did it for you.
So settings.py, the one line we had to add there, is also we need to tell it, you know,
besides the App Engine Django app and authentication app, we want the to-do app, which is the one
we're writing.
So inside the to-do sub directory, we have an empty file that says, hey, this is a Python
module, let us run Python code here.
And then we have models.py, views.py, and templates, which is the data, the controllers
remember, in Django, when they say views, they mean controllers, and then templates.
So let me just give a quick demo here, and what the application looks like that we're
going to be talking about.
So the index of the application is just lift, just lift your to-do list.
You can have multiple to-do list.
And then we have the box to make a new one.
So let's make a new one here.
We've got a second one, and we can click on the list.
We have items in them that we can might finish.
Let's do that here, and my total is in finished, and they're, you know, my finished, we can
create a new one, and that's all the application does.
So we've got basically three actions, we've got two pages and three actions on them.
So the pages are the index in this one, and the actions are created a new list, created
a new item, and mark that in finished.
So we'll be going over the code that does these now.
So here's two objects that we're dealing with, we're dealing with list objects and item
objects.
So in the list, we just have a title, and we have a user property.
Now you're familiar with whatever programming language you're in, it has strings, so we
won't cover that.
But you probably haven't seen user properties before.
So this is one of the cool things about App Engine, is it's seamlessly integrated with
the Google account API.
So all you have to do is say, now this is a user property, and we'll see a little bit
of code that we use to hit this into our application.
And then it's automatically, anyone with a Google account can log into your application,
and you'll know who they are, and you can associate content with them.
And then we have the list items, which we have a string property again, we have a date
time property that says when it was finished, and then we have a reference property.
So the reference property is kind of the stupid half-brother of foreign keys in a relational
database.
And what this gives you is it says that I'm related to a list, and then we also can give
a collection name equals items, and what that does is it puts a property into new list
that I didn't have to declare, so I can say list.items, and it will query for the items
that are associated through this reference property.
So that's the reason I say that this is kind of the stupid half-brother, is this two
things it doesn't do.
One is it doesn't do referential integrity.
If I have a list that has a bunch of items, I can blow the list away, and if I'm not careful
in my code, then I can just leave these items orphaned with an invalid reference.
It will not stop me from doing that.
So if you're using my SQL, you're probably used to that, but otherwise, that's not something
you like.
The other thing is that in a modern object relational matter, if you have a list items, you
can say items.append and put a new item in it, and it will take care of writing that
to the database for you.
Not so here.
The dot items property that it generates will only let you query it, only let you iterate
through those items, and you can filter on them and give them, you can do an order by,
you can't modify it through that list.
So in that respect, it's kind of weak.
So those are our models.
This is the whole model.
This is the whole file.
There's nothing else in there.
Now I'm going to talk about the views, which is, again, remember, views means controllers,
and I'm going to go through this piece at a time, because it's a larger file.
So first I have a couple of forms declared.
So this is part of Google App Engine, implemented something called Django Form.
So it's not part of Django, but it's inspired by Django, and it looks a lot like the Django
Forms API.
So a model form means we're going to make a form based on these models that we just declared.
So all we have to do is we have a class meta, and we say what model it belongs to, and
then we can either include or exclude attributes off of that model.
And I like to use exclude, because then if I go back here and I add a new attribute,
it will automatically be in my form, and I don't have to do anything else.
So I like to exclude one they don't want, rather than explicitly enumerating the ones
they do want, but either way is supported.
So we're going to exclude the user property there, because we're going to get that from
the authentication API, and here we're going to exclude the reference property, and we're
going to exclude the finished part, because that's going to be done by a separate one.
So the controller, so this is the index controller.
So this is what controls this, not this page, but this one.
So we're going to have a list of our 2D lists, and then we're going to have a new list
capability.
So this part here, where we say every question method equals post, that's creating a new
list.
So we're going to skip this for now, and just look at what we have to do if we're just
rendering the page.
So what we do is these two here, 2D lists, but GQL, and query 2D lists, I commented out
one of them, because there are two ways of doing the same thing.
And I wanted to show you that there's kind of two data APIs that App Engine exposes.
So this kind of, you build strings and execute them.
So I'd like to do the other API better, it's kind of more object oriented, more modern,
but Google is like, oh, that might be scary to people.
So we're going to let you write GQL instead of SQL.
So it kind of looks like SQL, and that's what that is.
And then the other one, we have a dot order function that takes the attribute to order
function, that's a dot filter, and that's pretty much all you can do with them.
And then what we're going to do is that we have a list, or in this case, are iterable,
meaning it's not a list yet, but it knows how to create, it knows how to get a list.
We're going to return the template, so this is our template name, and then we're going
to do something called local.
That's because render to response takes a template an dictionary, meaning a hash table
of values that the template cares about.
So let's just look at that template really, really quickly.
The template cares about a variable called to-dos, and it cares about a variable called form.
So we could either explicitly put those in a dictionary, or we could use this Python
function called locals, and what that does is, take the variables in my local name space
and put them in a dictionary where the keys are the variable names, and the values are
the variable values.
So we've seen two dudes that the template cares about, and let's look at where the form
comes from.
Again, if it's posed, we're going to create something, we're not going to look at that right
now.
We're going to look at what we do when we don't create a new one, we just form equals to-do
form.
So that's what we said, class to-do form, that's what it's talking about.
That's all we need to do to create the form object.
And then when we're in our template, we need to put the form in the template by using
bracket bracket form.
So this is kind of the equivalent of PHP, no bracket percent equals.
It says, no, print this into my page.
And then you'll see it, but around it, we have table tags, and that's because the general
forms use tables to make the forms line up nicely by default.
So if your CSS bigot, you might have a problem with that, but it does make-the benefit for
me is that I don't have to include any CSS to make things look halfway decent.
But it does make things a little cranky, and that, you know, I have to kind of know what
the table is doing to put my TRTD tags in for my little inputs to knit there.
Okay, so that's the form, and then the listing the to-do is, we just have a for loop, and
then we have-we're just going to put in an HREP to the page dealing with that list, and
put the to-do dot title in it, and that's all there is to it.
I do want to mention this, this looks like Python, if you're familiar with Python, it looks
like Python, but it's not.
Daniel templates are not Python code.
These things where you have a bracket percent, this is called a template tag.
This template tag is a for tag, and it happens to accept code that looks like a Python
for loop, but it's not really.
So for instance, if you wanted to do an if, there is something that's an if tag, and
you can just say if Boolean, you can't say if Boolean equals condition, no, you can
just say if Boolean.
If you want to do an equals equals, you need to use the if equal tag instead.
So it starts to break down.
It's not really Python, and so as soon as you know that the less confused you're going
to be.
So let's look at what happens when the user creates a new list.
So what happens is it comes in with the classical post, because that's what I said here in
my form, method equals post.
So we come in here, we instantiate the form, the next time we pass the post variables
to the form, so it knows what data to use, and then we say if form is valid, then we
say form.save, and what we're going to do here is we're going to say commit equals
file.
So what I said is I'm going to modify this object that's created for me afterwards,
and I will take care of putting it in the database.
So if I took the commit equals file, it wouldn't be incorrect, but it would be inefficient
because it would be saved once here, and then once here, which is put as the app engine
data store, you know, save this to the database call.
And that works whether it's a new object or an existing object.
So notice that form.save, we said form is a two new form, and it knows about two
new lists, so save will return a new object of whatever type we declared, and it will
stick the data from a quest.post into the appropriate attribute.
So then the other thing, we had the reason we said commit equals file here, because we
want to set the user attribute, and so what we're going to do is we're going to call
getCurrentUver, this is from the app engine API, and stick that into the user attribute.
So the only thing you might be saying, well, what if there isn't a logged in user, haven't
I just stuck a no or a none attribute here?
Well, up here we have login required, this is called a decorator in Python, and decorators
are something that their functions that can wrap other functions with code.
And so this particular one wraps the index function in code that says if the current
user is none, then redirect and make in log in, that's what this does.
So that's all the code I had to write to integrate this with Google accounts.
So what that does is, let me see if I can, no, I don't have a, I'm not actually not going
to demonstrate log out, because I would take too much time, but what you can do is when
you run this for yourself, that's my presentation, when you run this for yourself, you'll notice
that the first thing it does when you go to the index is it will ask you to log in, and
since you run the development server, it doesn't do any actual authentication, whatever email
address you give it, it will say, okay, you're good, but when you upload it to Google
App Engine, you would actually authenticate versus Google accounts.
And I didn't mention this, but this is what you run once you un-vip that code, and for
those of you who just came in, the code is up there, just go to slash JLS, you'll get
a directory listing, everything that starts out with Google App Engine, that's what you
want.
And then you'll see these as to the directory you un-vip and my managed up high run server,
and then you go to port 8,000 on localhost.
So back to this, we're done, we're done talking about the index page, so let's talk about
what happens when we go in, when we go into a list, and mark some items finished, so I'm
going to, I have my checkbox, and I have my mark button marked things finished, sorry,
this is a new laptop, and not 100% used to it yet, all right, okay, so the controller
says, but when we get to this method, we know we're submitting a form, so I didn't bother
with any if request equals post, every time this is called, it's submitting a form.
So what we're going to do is, this time we're not using a model form, we're rolling our
own hard way, which sometimes have to do, and so what that looks like is, we're going
to say request.post.getList, and then this is the name of the checkbox, so what does
this, not it, you can have multiple checkboxes submitted at a time, then we're going to get
all of them, and then for each of those, we're going to turn it into an integer, and then
these brackets here, turn that into a list, and pass that to get item by ID, so get item
by ID that basically, you know, get, fetch an object by its key, and so it can either take
one object, or, sorry, one integer, or it can take a list of integers, and we're passing
the list of integers, and it just knows what to do with either of those, so we put that
into our items variable, and then for each of those, then we set the finish date, and
we save it, so again, this is an existing item, but we still use .put to put it back in
the database, so there's a couple things here that you might be thinking, oh, this is different
if you know how relational databases work, and relational database, I'd just say update
to the items, set finish equals current time stamp, where ID in this list, is one statement,
I can't do that here, I have to read the items in, loop through them, set the finish and
save it, explicitly, so, you're kind of seeing the dark side of the data story at PINL,
is that, you know, it is an object database, it's not a relational database, and you have
to pull the objects in to manipulate them, the other thing to note here is that they tend
up now, isn't always what you might think, and what that means is, because data storage
in distributed database, or rather big tables in distributed database, they tend up now
will be different things on different machines, so if you have two requests coming in, and one
of them runs on one machine, and one runs on another, you know, if you rely on the first
request having its item spinach before the second one, then you might be in first
surprise, so there's more information on Google's side about, you know, how to deal with
this, I won't go into the details there except to note that you want to be aware of it.
If it's more than a few seconds off, and they do the same things like your NTP, but,
you know, if you're talking about millisecond level, then, you know, nothing you can do
about that.
Right, but nothing like, I wouldn't expect to be a minute off, I wouldn't either, I wouldn't
either.
So, let's look at how these functions that we've been looking at, these are Python functions
with March finished, and index, let's look at how these functions get mapped to URL.
So that's in something called URL.py, it's in the root of the application, it's not in
the 2D sub directory, and it's just, there's a single line that says URL patterns equals
patterns, there's a function called, and says look for these functions in this Python path,
so to speak.
So it's in the 2D sub directory, in view.py, so we leave the views off, or we leave the
dot.py off, and we just use a dot instead of, instead of slash.
And so then we map, this regular expression just says beginning of string end of string,
if there's nothing there, we go to index, and we say, if it starts with a list, then
have slash D plus, that's slash D plus, that's just a sequence of integers, then we go
to the 2D list represented by this key, and what this part is, is it says, let's name
this regular expression group, and they were going to name it ID, and the reason for that
is, this is optional, but the reason for this is, we have parameters in the 2D function,
which I didn't show you, and I won't be, because it doesn't really show anything
else interesting, but there's an ID parameter there, and so it's saying, this is the group
that we want to turn into that parameter, and that's optional, because if we leave it
off, then we'll just pass the groups in as parameters, and the order they were given,
but I think it's a little more maintainable, if we name them, and then we'll remind
it when we're looking at that, what's going on.
So if you're saying that regular expression seems like a terrible way to specify URL, I'm
kind of with you there, but that's how it works, so, and it's not too bad if you're
good with regular expressions, and then we're going to map Mark's finish to Mark's finish
that's straightforward, so like I said, I skipped the 2D method and the template in order
to give us more time to write some code, so, assuming, so who has the App Engine SDK installed?
All right, everyone gets to go download that, so just remember if you're, they don't say
this on their site, but if you download it on Linux, everyone else gets an installer,
Linux doesn't, you get a zip file, unzip it, and just move the Google underscore App Engine
directory that comes out, move that to user local, because that's where the server code
looks for it, so just put it in user local, and then I've got the zip file of code over
there, any questions while, while we're getting started on that?
I'll give you a second to think about questions, and while you're doing that, let me give,
let me, I'm going to use our Python user group here, use the Python.org, we have an ISC
channel, it's very active, if you decide, hey, this App Engine stuff is cool, but I don't
really know Python, you know, come over here and ask us, there's almost always someone
there, and you know, we're really helpful, we also have a mailing list, it's more traffic
because we like the instant gratification of IRC. Also, I went for a company called Feature 50,
we do cool stuff, we do good hardware, we have 20% time, we work the famous people,
send me a resume, and I won't go, I won't believe that anymore, so, so, we do Java and JavaScript
as well, so I will leave this up, and I will go to write a link, while you guys are
downloading the SDK, and so forth.
.
.