- MCP server with stdio transport for local use - Search episodes, transcripts, hosts, and series - 4,511 episodes with metadata and transcripts - Data loader with in-memory JSON storage 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
380 lines
25 KiB
Plaintext
380 lines
25 KiB
Plaintext
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.
|
|
.
|
|
.
|