2
Google App Engine in 40 Minutes
(The Absolute Essentials)
Paul Barry – Institute of Technology, Carlow in Ireland
PyCon Ireland 2011
Grab the slides:
http://paulbarry.itcarlow.ie/GAE.pdf
4
5
6
The Absolute Essentials in 40 Minutes...
7
What is Google App Engine?
8
Cloud-based Application Deployment Environment
9
Integrated Collection of Google APIs
10
Guido's Playground
11
Is App Engine “just another framework”?
12
It is all about your data...
13
Think of App Engine as a highly scalable,distributed database server that you can program
with Python 2.5...
14
Think of App Engine as a highly scalable,distributed database server that you can program
with Python 2.5...
and (cough) Java and (cough, cough) Go
15
MVC
16
Let's solve a real problem...
17
18
19
20
21
Step 1
Download the SDK from:
http://code.google.com/appengine/
and sign-up for an App Engine account ID
22
Step 2
Create a new project...
23
...by creating an app.yaml file
application: dwwgapp version: 1 runtime: python api_version: 1
handlers: - url: /static static_dir: static
- url: /.* script: dwwgapp.py
24
Step 3
Model Your Data
25
Create dwwgDB.py (1 of 3)
from google.appengine.ext import db
26
Create dwwgDB.py (2 of 3)
from google.appengine.ext import db
class Sighting(db.Model):
27
28
Create dwwgDB.py (3 of 3)
from google.appengine.ext import db
class Sighting(db.Model):name = db.StringProperty()email = db.StringProperty()date = db.DateProperty()time = db.TimeProperty()location = db.StringProperty()fin_type = db.StringProperty()whale_type = db.StringProperty()blow_type = db.StringProperty()wave_type = db.StringProperty()
29
Step 4
Define your UI in HTML
30
The header.html template
<html> <head>
<title>{{ title }}</title> </head> <body>
<h1>{{ title }}</h1>
31
The footer.html template
<p> {{ links }} </p> </body>
</html>
32
The form_start.html template
<form method=”POST” action=”/”> <table>
33
The form_end.html
<tr><th> </th><td><input type="submit"
value="{{ sub_title }}"></td></tr>
</table> </form>
34
Render some HTML
from google.appengine.ext.webapp import template
html = template.render('templates/header.html', {'title': 'Report a Possible Sighting'})
35
Rendering a Page
from google.appengine.ext.webapp import template
html = template.render('templates/header.html',{'title': 'Report a Possible Sighting'})
html = html + template.render('templates/form_start.html', {})
# Do something in here to render the form?!?!?!?!?!?!?
html = html + template.render('templates/form_end.html',{'sub_title': 'Submit Sighting'})
html = html + template.render('templates/footer.html', {'links': ''})
36
Step 5
Write code to render your form
37
Django Forms to the Rescue!
from google.appengine.ext.db import djangoforms
import dwwgDB
class SightingForm(djangoforms.ModelForm):class Meta:
model = dwwgDB.Sighting
38
Rendering a Page, Again
from google.appengine.ext.webapp import template
html = template.render('templates/header.html',{'title': 'Report a Possible Sighting'})
html = html + template.render('templates/form_start.html', {})
html = html + str(SightingForm())
html = html + template.render('templates/form_end.html',{'sub_title': 'Submit Sighting'})
html = html + template.render('templates/footer.html', {'links': ''})
39
Step 6
Tie it all together with logic
40
You need to start your webapp
from google.appengine.ext import webappfrom google.appengine.ext.webapp.util import run_wsgi_app
…
app = webapp.WSGIApplication([('/.*', SightingInputPage)], debug=True)
def main(): run_wsgi_app(app)
if __name__ == '__main__': main()
41
Remember the app.yaml file?
application: dwwgapp version: 1 runtime: python api_version: 1
handlers: - url: /static static_dir: static
- url: /.* script: dwwgapp.py
42
Put this code in dwwgapp.py
from google.appengine.ext import webappfrom google.appengine.ext.webapp.util import run_wsgi_app
…
app = webapp.WSGIApplication([('/.*', SightingInputPage)], debug=True)
def main(): run_wsgi_app(app)
if __name__ == '__main__': main()
43
What's this SightingInputPage thing?
44
class SightingInputPage(webapp.RequestHandler): def get(self): html = template.render('templates/header.html',
{'title': 'Report a Possible Sighting'}) html = html + template.render('templates/form_start.html', {}) html = html + str(SightingForm()) html = html + template.render('templates/form_end.html',
{'sub_title': 'Submit Sighting'}) html = html + template.render('templates/footer.html', {'links': ''}) self.response.out.write(html)
Responding to a GET Request
45
Step 7
Take your local app for a spin
46
Click the “Run” button...
or
$ dev_appserver.py dwwgapp
47
Ta Da! :-(
48
Looks kinda shitty, doesn't it?
49
Add a little CSS goodness..
<link type="text/css" rel="stylesheet" href="/static/dwwg.css" /><link type="text/css" rel="stylesheet" href="/static/styledform.css" />
50
...and some code to dwwgDB.py
_FINS = ['Falcate', 'Triangular', 'Rounded'] _WHALES = ['Humpback', 'Orca', 'Blue', 'Killer', 'Beluga', 'Fin', 'Gray', 'Sperm'] _BLOWS = ['Tall', 'Bushy', 'Dense'] _WAVES = ['Flat', 'Small', 'Moderate', 'Large', 'Breaking', 'High']
...
location = db.StringProperty(multiline=True)fin_type = db.StringProperty(choices=_FINS) whale_type = db.StringProperty(choices=_WHALES) blow_type = db.StringProperty(choices=_BLOWS) wave_type = db.StringProperty(choices=_WAVES)
51
Ta Da! :-)
52
Step 8
Do something with your data
53
You need to POST data!
POST data with put()
54
class SightingInputPage(webapp.RequestHandler): def get(self): html = template.render('templates/header.html',
{'title': 'Report a Possible Sighting'}) html = html + template.render('templates/form_start.html', {}) html = html + str(SightingForm()) html = html + template.render('templates/form_end.html',
{'sub_title': 'Submit Sighting'}) html = html + template.render('templates/footer.html', {'links': ''}) self.response.out.write(html)
55
class SightingInputPage(webapp.RequestHandler): def get(self): html = template.render('templates/header.html',
{'title': 'Report a Possible Sighting'}) html = html + template.render('templates/form_start.html', {}) html = html + str(SightingForm()) html = html + template.render('templates/form_end.html',
{'sub_title': 'Submit Sighting'}) html = html + template.render('templates/footer.html', {'links': ''}) self.response.out.write(html)
def post(self):
56
class SightingInputPage(webapp.RequestHandler): def get(self): html = template.render('templates/header.html',
{'title': 'Report a Possible Sighting'}) html = html + template.render('templates/form_start.html', {}) html = html + str(SightingForm()) html = html + template.render('templates/form_end.html',
{'sub_title': 'Submit Sighting'}) html = html + template.render('templates/footer.html', {'links': ''}) self.response.out.write(html)
def post(self): new_sighting = dwwgDB.Sighting()
57
class SightingInputPage(webapp.RequestHandler): def get(self): html = template.render('templates/header.html',
{'title': 'Report a Possible Sighting'}) html = html + template.render('templates/form_start.html', {}) html = html + str(SightingForm()) html = html + template.render('templates/form_end.html',
{'sub_title': 'Submit Sighting'}) html = html + template.render('templates/footer.html', {'links': ''}) self.response.out.write(html)
def post(self): new_sighting = dwwgDB.Sighting() new_sighting.name = self.request.get('name') new_sighting.email = self.request.get('email') new_sighting.date = self.request.get('date') new_sighting.time = self.request.get('time') new_sighting.location = self.request.get('location') new_sighting.fin_type = self.request.get('fin_type') new_sighting.whale_type = self.request.get('whale_type') new_sighting.blow_type =self.request.get('blow_type') new_sighting.wave_type = self.request.get('wave_type')
58
class SightingInputPage(webapp.RequestHandler): def get(self): html = template.render('templates/header.html',
{'title': 'Report a Possible Sighting'}) html = html + template.render('templates/form_start.html', {}) html = html + str(SightingForm()) html = html + template.render('templates/form_end.html',
{'sub_title': 'Submit Sighting'}) html = html + template.render('templates/footer.html', {'links': ''}) self.response.out.write(html)
def post(self): new_sighting = dwwgDB.Sighting() new_sighting.name = self.request.get('name') new_sighting.email = self.request.get('email') new_sighting.date = self.request.get('date') new_sighting.time = self.request.get('time') new_sighting.location = self.request.get('location') new_sighting.fin_type = self.request.get('fin_type') new_sighting.whale_type = self.request.get('whale_type') new_sighting.blow_type =self.request.get('blow_type') new_sighting.wave_type = self.request.get('wave_type')
new_sighting.put()
59
html = template.render('templates/header.html', {'title': 'Thank you!'}) html = html + "<p>Thank you for providing your sighting data.</p>" html = html + template.render('templates/footer.html', {'links': 'Enter <a href="/">another sighting</a>.'}) self.response.out.write(html)
And don't forget to say “Thanks!”
60
61
Step 9
Deploy to Google's cloud
62
Click the “Deploy” button...
or
$ appcfg.py upload dwwgapp/
63
http://dwwgapp.appspot.com
64
Step 10
Realise your not done...
65
Damn That Spam!
Some smart-alec posted a sightingof a Great White Shark on the top
of Mount Leinster...
66
What to do?
67
A tiny config tweak to app.yaml...
application: dwwgapp version: 1 runtime: python api_version: 1
handlers: - url: /static static_dir: static
- url: /.* script: dwwgapp.py login: required
68
...and two tiny code changes
Add this to the dwwgDB.py model:
which_user = db.UserProperty()
Add this to your post() method in dwwgapp.py:
new_sighting.which_user = users.get_current_user()
69
http://dwwgapp.appspot.com
70
http://appengine.google.com
71
And after all that,what do you end up with?
72
Happy Clients!
73
Want to learn moreabout this talk's example code?
74
Head First Python
75
Don't forget to check out the App Engine Tutorial on Sunday
with Seán Murphy
76