Home Tutorials Training Consulting Books Company Contact us


Get more...

Google App Engine - Python. This article describes the creation of a web application with Python on the Google App Engine. The example created in this article will be a simple Todo list. The usage of the email API will get demonstrated. Also conditions in the Django web template and formating of dates will get demonstrated. The article demonstrates optionally the usage of Eclipse for developing, running and debugging the Google App Engine application.

1. Overview

Google offers with the Google App Engine(GAE) a cloud computing infrastructure for creating and running web application on the Google infrastructure. The GAE is a platform to create web applications. Google does currently support Python, Java and several languages based on Java as a programming language for the Google App Engine. This article will focus on the development with Python but no previous knowledge of Python is required. See the resource part of a link to an introduction to the Google Apps Engine Development with Java. Cloud computing allows that system resources can be dynamically allocated to the application on demand. Currently the price model for the Google App Engine is not defined but Google offers a free starting offer for the GAE. Currently a user can create a maximum of 10 application on the Google App Engine.

In case you want to use Eclipse only for standard Python development'(not for the Google App Engine) please see Python Development with Eclipse.

2. Installation

Download and install Python 2.5.2. This is the version that runs on the Google App Engine and should therefore be used http://www.python.org/download/releases/2.5.2/.

Download the Python version of the Google App Engine from Download Site for the GAE.

The main page for the GAE is https://cloud.google.com/appengine/downloads.

On the main page you need to created an account if you have not yet done so.

3. Developing your first application

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 a URL and a description to each todo.

I suggest to use Eclipse with Pydevas IDE as Python interprets intension as inclination of blocks and therefore you may run into problems copying code from this tutorial if you use a standard text editor.

todo10

The source of this example is stored in project "de.vogella.gae.python.todo" and can be browsed on www.vogella.com source code.

3.1. Directory

Create a new directory "googleappengine01", e.g. c:\temp\googleappengine01. All the following files must be created within this directory.

3.2. Configuration File

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.

3.3. Create the application

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

Email notification does not work in the local version. This feature is only available after the upload to the Google cloud.

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.

This uses the Django template language. See the appendix for an weblink for the Django template language.
<!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.

3.4. Run your application

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/
You can edit your python source code and just refresh the browser to see the updates.
To stop the server press Ctrl+Break on Windows, Unix and Linux using the standard Ctrl+C.

3.5. Clean your test data

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/

3.6. Deploy your application

Now deploy your application to the Google cloud. Open the following URL http://appengine.google.com/]] 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/

4. Using Eclipse

You can also use Eclipse for developing GAE applications via the PyDev plugin.

4.1. Eclipse and Pydev

Please see Python Development with Eclipse to learn how to install and use Eclipse and Pydev. The remaining focuses on the additional setup to use the GAE.

4.2. Project

Create a Python project called "de.vogella.gae.python.todo". See Python Development with Eclipse for how to create a new Pydev / Python project in Eclipse.

Use Python version 2.5. Remember that the GAE does currently only support Python version 2.5 Copy the content of the directory from the previous example (all your files and directeries): todo.py, app.yam, index.html)into the source folder.

eclipse50

4.3. Make GAE SDK available

We need to add the GAE SDK to the pythonpath. Right-click your project and select properties. Select PYTHONPATH and press "Add source folder" in the "External Source Folder". Select the installation directory of the GAE SDK. Also add lib/django, lib/webob and lib/yaml.

eclipse60

4.4. Run your application

You still can use the command line to run your GAE application. But we are now going to configure Eclipse to allow you to run your application directly from Eclipse. Right-click on "todo.py", select Run As  Run Configuration. Under Main Module maintain the path to dev_appserver.py.

eclipse70

Switch to the argument tab and maintain the full path name of your project as a parameter. Put the parameter in double-quotes.

eclipse80

Click run to start your application. If you have the console view open Window  Show View  Console you should see the familiar output of the GAE and be able to open the application in your browser as before.

5. Monitor your application

Google provides a monitor application http://appengine.google.com/.

6. Links and Literature