Free tutorials for Java, Eclipse and Web programming



Follow me on twitter

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 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

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

Tip

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.

Tip

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/

Tip

You can edit your python source code and just refresh the browser to see the updates.

Tip

To stop the server press Cntr+Break on Windows, Unix and Linux using the standard Cntr+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/ 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/