| Free tutorials for Java, Eclipse and Web programming |
In this part of the tutorial we will develop a small Todo list. This list will allow you to store todos, send out email reminders to yourself and delete todos. You can also store an URL and a description to each todo.

The source of this example is stored in project "de.vogella.gae.python.todo" and can be browsed on www.vogella.de source code
Create a new directory "googleappengine01", e.g. c:\temp\googleappengine01. All the following files must be created within this directory.
Create the following configuration file for your application. It define the application name, the runtime and defines the handler script which should get invoked for a specific URL.
application: googleappengine01 version: 1 runtime: python api_version: 1 handlers: - url: /css static_dir: css - url: /images static_dir: images - url: /.* script: todo.py
The script defines that all URL request will be handled by the script "todo.py". It also defines two static directories in which static content is stored which can also be served. Without this definition the Google app engine would not deliver this content to the webbrowser.
Python allows to define several classes in one source file. We are going to create the following classes:
TodoModel: Defines the data model for your application.
MainPage: Serves as the main entry point, user validation is checked here
New: This class will create the new Todo in the database
Done: Marks the todo as complete and deletes it from the database
Email: Sends the selected todo to yourself via email as a reminder
Create the file "todo.py" with the following content. Study the source code; I tried to document it well in the hope that it is self-explaining.
from google.appengine.api import users
from google.appengine.ext import webapp
from google.appengine.ext.webapp.util import run_wsgi_app
from google.appengine.ext import db
from google.appengine.ext.webapp import template
from google.appengine.api import mail
# Todo defines the data model for the Todos
# as it extends db.model the content of the class will automatically stored
class TodoModel(db.Model):
author = db.UserProperty(required=True)
shortDescription = db.StringProperty(required=True)
longDescription = db.StringProperty(multiline=True)
url = db.StringProperty()
created = db.DateTimeProperty(auto_now_add=True)
updated = db.DateTimeProperty(auto_now=True)
dueDate = db.StringProperty(required=True)
finished = db.BooleanProperty()
# The main page where the user can login and logout
# MainPage is a subclass of webapp.RequestHandler and overwrites the get method
class MainPage(webapp.RequestHandler):
def get(self):
user = users.get_current_user()
url = users.create_login_url(self.request.uri)
url_linktext = 'Login'
if user:
url = users.create_logout_url(self.request.uri)
url_linktext = 'Logout'
# GQL is similar to SQL
todos = TodoModel.gql("WHERE author = :author and finished=false",
author=users.get_current_user())
values = {
'todos': todos,
'numbertodos' : todos.count(),
'user': user,
'url': url,
'url_linktext': url_linktext,
}
self.response.out.write(template.render('index.html', values))
# This class creates a new Todo item
class New(webapp.RequestHandler):
def post(self):
user = users.get_current_user()
if user:
testurl = self.request.get('url')
if not testurl.startswith("http://") and testurl:
testurl = "http://"+ testurl
todo = TodoModel(
author = users.get_current_user(),
shortDescription = self.request.get('shortDescription'),
longDescription = self.request.get('longDescription'),
dueDate = self.request.get('dueDate'),
url = testurl,
finished = False)
todo.put();
self.redirect('/')
# This class deletes the selected Todo
class Done(webapp.RequestHandler):
def get(self):
user = users.get_current_user()
if user:
raw_id = self.request.get('id')
id = int(raw_id)
todo = TodoModel.get_by_id(id)
todo.delete()
self.redirect('/')
#This class emails the task to yourself
class Email(webapp.RequestHandler):
def get(self):
user = users.get_current_user()
if user:
raw_id = self.request.get('id')
id = int(raw_id)
todo = TodoModel.get_by_id(id)
message = mail.EmailMessage(sender=user.email(),
subject=todo.shortDescription)
message.to = user.email()
message.body = todo.longDescription
message.send()
self.redirect('/')
# Register the URL with the responsible classes
application = webapp.WSGIApplication(
[('/', MainPage),
('/new', New),
('/done', Done),
('/email', Email)],
debug=True)
# Register the wsgi application to run
def main():
run_wsgi_app(application)
if __name__ == "__main__":
main()
This class MainPage uses a HTML template "index.html" for the rendering. Create the following file.
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
<head>
<title>Todos</title>
<link rel="stylesheet" type="text/css" href="css/main.css"/>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"></meta>
</head>
<body>
<div style="width: 100%;">
<div class="topLine">
<div style="float: left;"><img src="images/todo.png" /></div>
<div style="float: left;" class="headline">Todos</div>
<div style="float: right;"><a href="{{ url }}">{{ url_linktext }}</a> {{user.nickname}}</div>
</div>
</div>
<div style="clear: both;"/>
{# Check if we have any todos, only in this case render the table #}
{% if numbertodos %}
You have a total number of {{numbertodos}} Todos.
<table>
<tr>
<th>Short description </th>
<th>Due Date</th>
<th>Long Description</th>
<th>URL</th>
<th>Created</th>
<th>Updated</th>
<th>Done</th>
<th>Send Email reminder</th>
</tr>
{% for todo in todos %}
<tr>
<td>
{{todo.shortDescription}}
</td>
<td>
{{todo.dueDate}}
</td>
<td>
{{todo.longDescription}}
</td>
<td>
{% if todo.url %}
<a href="{{todo.url}}" > {{todo.url}}</a>
{% endif %}
</td>
<td>
{{todo.created|date:"d.m.Y"}}
</td>
<td>
{{todo.updated|date:"d.m.Y"}}
</td>
<td>
<a class="done" href="/done?id={{todo.key.id}}" >Done</a>
</td>
<td>
<a class="email" href="/email?id={{todo.key.id}}" >Email</a>
</td>
</tr>
{% endfor %}
</table>
{% endif %}
<hr />
<div class="headline">Create new Todo?</div>
{% if user %}
<form action="/new" method="post">
<table>
<tr>
<td><label for="shortDescription">Summary</label></td>
<td><input type="text" name="shortDescription" id="shortDescriptions" size="80"/></td>
</tr>
<tr>
<td><label for="dueDate">Due Date</label></td>
<td><input type="dueDate" name="dueDate" id="dueDate"/></td>
</tr>
<tr>
<td valign="top"><label for="longDescription">Description</label></td>
<td><textarea rows="4" cols="80" name="longDescription" id="longDescription"></textarea></td>
</tr>
<tr>
<td><label for="url">URL</label></td>
<td><input type="text" name="url" id="url" size="80"/></td>
</tr>
<tr>
<td colspan="2" align="right"><input type="submit" value="Create"/></td>
</tr>
</table>
</form>
{% else %}
Login with your Google Account to create and review todos.
{% endif%}
</body>
</html>
Also create the directory css and put in this file "main.css". This is the style sheet used to make index.html "pretty".
body {
margin: 5px;
font-family: Arial, Helvetica, sans-serif;
}
hr {
border: 0;
background-color: #DDDDDD;
height: 1px;
margin: 5px;
}
table th {
background:#EFEFEF none repeat scroll 0 0;
border-top:1px solid #CCCCCC;
font-size:small;
padding-left:5px;
padding-right:4px;
padding-top:4px;
vertical-align:top;
text-align:left;
}
table tr {
background-color: #e5ecf9;
font-size:small;
}
.topLine {
height: 1.25em;
background-color: #e5ecf9;
padding-left: 4px;
padding-right: 4px;
padding-bottom: 3px;
padding-top: 2px;
}
.headline {
font-weight: bold;
color: #3366cc;
}
.done {
font-size: x-small;
vertical-align: top;
}
.email {
font-size: x-small;
vertical-align: top;
}
form td {
font-size: smaller;
}
Optional you can put a graphic "todo.png" into the images directory.
Switch to a command line. Switch to the directory which contains your application directory. Start your application locally with the following command.
dev_appserver.py googleappengine01/
Switch to a browser and access your local host via: http://localhost:8080/
The local installation will store your test data until you tell the engine to delete the test data. Use therefore the --clear_datastore option during startup.
dev_appserver.py --clear_datastore googleappengine01/
Now deploy your application to the Google cloud. Open the following URL http://appengine.google.com/ and login with your Google User. Press the button "Create an application".
You need then to verify your account via an SMS.
Create an application name. You have to choose one which is still available. After you found one you need to change the application in the app.yaml file to this new name.
Use now the command line to upload your application.
appcfg.py update googleappengine01/
Thats it. You should now be able to find your application at http://yourname.appspot.com/. For example this example can be found at http://myvogella.appspot.com/