Sunday, 1 August 2010

Python web development for Google App Engine

To start your very first Python web development for GAE, you will need:
To run any application you should have GAE account (http://code.google.com/intl/pl/appengine/). You should also create new application on GAE administration pages.



Hello World project
Now, you can try to generate Hello World application using the Pydev's wizard. Create new project in eclipse - use "Pydev Google App Engine Project". Select Python version to 2.5 and configure interpreter to the one that you installed earlier. Next, you will have to specify path to Google App Engine SDK. On next page, set name for your application (the same as was registered on GAE account admin pages) and choose "Hello Webapp World" template. Within few seconds you should have your Hello World web app generated.



Run Google App Engine Launcher that was installed with SDK. If you are running it first time, configure paths to Python and SDK (Edit -> Preferences). Add your project to the Launcher:



Now, you should be able to run your application locally as well as deploy it to GAE. During deployment you will need to log into your GAE account.



More complex application

As usual, I created my test app as simple Book management application. It is not even CRUD as I had no time to develope update functionality. Application requires user to log in using Google Account before they enter the main page.



There, user will have links to other pages:
  • Book list - when user will be able to display and remove books
  • New book - page with form for adding new entries
  • log out
Project structure:



Projects uses Google Webapp framework that is quite simple, but on the other hand you don't need anything more sophisticated for such simple app;)

Main application code and logic is in books.py:
import os

from google.appengine.api import users
from google.appengine.ext import webapp
from google.appengine.ext.webapp import template
from models import Book

from google.appengine.ext.webapp.util import run_wsgi_app

class MainPage(webapp.RequestHandler):
def get(self):
user = users.get_current_user()

if user:
template_values = {
'user' : user.nickname(),
'logoutUrl' : users.create_login_url("/")
}
path = os.path.join(os.path.dirname(__file__)+"/templates", 'index.html')
self.response.out.write(template.render(path, template_values))

else:
self.redirect(users.create_login_url(self.request.uri))

class NewBookPage(webapp.RequestHandler):
def get(self):
path = os.path.join(os.path.dirname(__file__)+"/templates", 'new.html')
self.response.out.write(template.render(path, {}))

class ListBooksPage(webapp.RequestHandler):
def get(self):
books = Book.all();
template_values = {
'books' : books,
}

path = os.path.join(os.path.dirname(__file__)+"/templates", 'list.html')
self.response.out.write(template.render(path, template_values))

class SaveBookPage(webapp.RequestHandler):
def post(self):
book = Book();
book.title = self.request.get('title')
book.author = self.request.get('author')
book.put()
self.redirect("/list")

class RemoveBookPage(webapp.RequestHandler):
def get(self):
book = Book.get(self.request.get('key'))
book.delete()
self.redirect("/list")

application = webapp.WSGIApplication(
[('/', MainPage),
('/list', ListBooksPage),
('/save', SaveBookPage),
('/remove', RemoveBookPage),
('/new', NewBookPage)],
debug=True)

def main():
run_wsgi_app(application)

if __name__ == "__main__":
main()
In this module we have main "controller" classes that cover all functionality. Classes are mapped to particular URL. Responsibility of each class is to perform business logic, prepare model and return template that will render the page:
  • MainPage - prepares model for menu, returns index.html template
  • NewBookPage - returns new.html template with form
  • ListBooksPage - selects books from Google Datastore, puts them into model and returns list.html template
  • SaveBookPage- saves book to datastore and redirects view to book list
  • RemoveBookPage - removes book from datastore and redirects view to book list
This module contains also code that maps each controller class to particular URL.

Pages are implemented using Django templated mechanism that is included in SDK. We have a main template base.html that renders html page structure:
<html>
<head>
<link type="text/css" rel="stylesheet" href="/style/main.css" />
</head>
<body>
{% block main %}
{% endblock %}
</body>
</html>
All other templates are extending the main one.

Code of index.html:
{% extends "base.html" %}
{% block main %}
Welcome {{ user }}!
<br/><br/>
Menu:<br/>
<a href="/list">Book list</a><br/>
<a href="/new">New book</a><br/>
<a href="{{ logoutUrl }}">Logout</a>
{% endblock %}
list.html:
{% extends "base.html" %}
{% block main %}
<table>
<tr>
<th>Title</th>
<th>Author</th>
<th></th>
</tr>
{% for book in books %}
<tr>
<td>{{ book.title }}</td>
<td>{{ book.author }}</td>
<td><a href="/remove?key={{ book.key }}">remove</a></td>
</tr>
{% endfor %}
</table>
<br/>
<a href="/">Back</a>
{% endblock %}
new.html:
{% extends "base.html" %}
{% block main %}
<form action="/save" method="POST">
<table>
<tr>
<td>Title:</td>
<td><input type="text" name="title"/></td>
</tr>
<tr>
<td>Author:</td>
<td><input type="text" name="author"/></td>
</tr>
<tr>
<td></td>
<td><input type="submit" value="Add"></td>
</tr>
</table>
</form>
<br/>
<a href="/">Back</a>
{% endblock %}
I used separete main.css file to define look (don't take it seriously hi hi) of the application:
body {
font-family: Verdana, Helvetica, sans-serif;
background-color: #DDDDDD;
}
In app.yaml file, we define that books.py module is responsible for handling all request to /* urls and that /style urls will be exposed as a static resource:
application: python-test-maciekm
version: 1
runtime: python
api_version: 1

handlers:
- url: /style
static_dir: style
- url: /.*
script: books.py
Data model is defined in models.py file:
from google.appengine.ext import db

class Book(db.Model):
title = db.StringProperty()
author = db.StringProperty()
Let's see the result
Such application should be ready to get it started using Launcher and for deployment on GAE. When it starts, you should be able to see application pages in the browser - creating new book:



And the list:



Was it hard? To be honest I really think it was not. Personally, I think it was even easier for me to create GAE application in Python than in Java. In the future, I would be happy to try to use Django instead of Webapp framework and to compare them.

1 comment:

  1. Mobile app gives a look to business app. A perfectly designed website always attracts more visitors. Even the simple factors like font color, font size plays a great role in making a mobile app attractive. We provide experienced team of website designers that lay emphasis on such minute details while designing.

    Cado Magenge
    ”http://appdevelopmentcompany.com.au/ipad-application-development.html”
    ”http://appdevelopmentcompany.com.au/custom-web-development.html”
    “http://appdevelopmentcompany.com.au/android-application-development.html”
    "http://www.appdevelopmentcompany.com.au/responsive-web-design.html"

    ReplyDelete