Flying with tipfy on Google App Engine
Hooray, there is a bonus part in the series (after a looooooooooooong wait =P)! On the first blog post, about Django, I received a comment about the use of tipfy, a small Python web framework made specifically for Google App Engine. Like Flask, tipfy is not a full stack framework and we will not use a database abstraction layer, we will use just the Google App Engine Datastore API, but tipfy was designed for Google App Engine, so it is less laborious to work with tipfy on App Engine.
First, we have to download tipfy. There are two options on official tipfy page: an all-in-one package and a do-it-yourself packaged. I am lazy, so I downloaded and used the all-in-one package =D That is so easy:
$ tar -xvzf tipfy.0.6.2.build.tar.gz
$ mv project gaeseries
After it, we go to the project folder and see the project structure provided by tipfy. There is a directory called “app”, where the App Engine app is located. The app.yaml file is in the app directory, so we open that file and change the application id and the application version. Here is the app.yaml file:
version: 4
runtime: python
api_version: 1
derived_file_type:
- python_precompiled
handlers:
- url: /(robots\.txt|favicon\.ico)
static_files: static/\1
upload: static/(.*)
- url: /remote_api
script: $PYTHON_LIB/google/appengine/ext/remote_api/handler.py
login: admin
- url: /_ah/queue/deferred
script: main.py
login: admin
- url: /.*
script: main.py
After this, we can start to code our application. tipfy deals with requests using handlers. A handler is a class who has methods to deal with different kinds of requests. That remember me a little the Strut Actions (blergh), but tipfy is a Python framework, what means that is easier to build web application using it! ;) Understanding tipfy: a URL is mapped to a handler that do something with the request and returns a response. So, we have to create two handlers: one for the posts listing and other for create a post, but let’s create first an application called blog, and a model called Post. Like Django, Flask and web2py, tipfy also works with applications inside a project.
To create an application, we just need to create a new Python package with the application name:
$ touch blog/__init__.py
After create the application structure, we install it by putting the application inside the “apps_installed” list on config.py file:
"""
config
~~~~~~
Configuration settings.
:copyright: 2009 by tipfy.org.
:license: BSD, see LICENSE for more details.
"""
config = {}
# Configurations for the 'tipfy' module.
config['tipfy'] = {
# Enable debugger. It will be loaded only in development.
'middleware': [
'tipfy.ext.debugger.DebuggerMiddleware',
],
# Enable the Hello, World! app example.
'apps_installed': [
'apps.hello_world',
'apps.blog',
],
}
See the line 22. Inside the application folder, let’s create a Python module called models.py. This module is exactly the same of Flask post:
class Post(db.Model):
title = db.StringProperty(required = True)
content = db.TextProperty(required = True)
when = db.DateTimeProperty(auto_now_add = True)
author = db.UserProperty(required = True)
After create the model, let’s start building the project by creating the post listing handler. The handlers will be in a module called handlers.py, inside the application folder. Here is the handlers.py code:
from tipfy import RequestHandler
from tipfy.ext.jinja2 import render_response
from models import Post
class PostListingHandler(RequestHandler):
def get(self):
posts = Post.all()
return render_response('list_posts.html', posts=posts)
See that we get a list containing all posts from the database and send it to the list_posts.html template. Like Flask, tipfy uses Jinja2 as template engine by default. Following the same way, let’s create a base.html file who represents the layout of the project. This file should be put inside the templates folder and contains the following code:
<head>
<meta http-equiv="Content-type" content="text/html; charset=utf-8"/>
<title>{% block title %}{% endblock %}</title>
</head>
<body id="">
{% block content %}
{% endblock %}
</body>
</html>
And now we can create the list_posts.html template extending the base.html template:
{% block title %}
Posts list
{% endblock %}
{% block content %}
Listing all posts:
<ul>
{% for post in posts %}
<li>
{{ post.title }} (written by {{ post.author.nickname() }})
{{ post.content }}
</li>
{% endfor %}
</ul>
{% endblock %}
Can we access the list of posts now by the URL? No, we can’t yet. Now we have to map the handler to a URL, and we will be able to access the list of posts through the browser. On tipfy, all URL mappings of an application are located in a Python module called urls.py. Create it with the following code:
def get_rules(app):
rules = [
Rule('/posts', endpoint='post-listing', handler='apps.blog.handlers.PostListingHandler'),
]
return rules
It is very simple: a Python module containing a function called get_rules, that receives the app object as parameter and return a list containing the rules of the application (each rule is an instance of tipfy.Rule class). Now we can finally see the empty post list on the browser, by running the App Engine development server and touching the http://localhost:8080/posts URL on the browser. Run the following command on the project root:
And check the browser at http://localhost:8080/posts :) And we see the empty list. Now, let’s create the protected handler which will create a new post. tipfy has an auth extension, who makes very easy to deal with authentication using the native Google App Engine users API. To use that, we need to configure the session extension, changing the conf.py module, by adding the following code lines:
'secret_key' : 'just_dev_testH978DAGV9B9sha_W92S',
}
Now we are ready to create the NewPostHandler. We will need to deal with forms, and tipfy has an extension for integration with WTForms, so we have to download and install WTForms and that extension in the project:
$ tar -xvf tip.tar.bz2
$ cp -r wtforms/wtforms/ ~/Projetos/gaeseries/app/lib/
$ wget http://pypi.python.org/packages/source/t/tipfy.ext.wtforms/tipfy.ext.wtforms-0.6.tar.gz
$ tar -xvzf tipfy.ext.wtforms-0.6.tar.gz
$ cp -r tipfy.ext.wtforms-0.6/tipfy ~/Projetos/gaeseries/app/distlib
Now we have WTForms extension installed and ready to be used. Let’s create the PostForm class, and then create the handler. I put both classes in the handlers.py file (yeah, including the form). Here is the PostForm class code:
csrf_protection = True
title = fields.TextField('Title', validators=[validators.Required()])
content = fields.TextAreaField('Content', validators=[validators.Required()])
Add this class to the handlers.py module:
middleware = [SessionMiddleware]
@login_required
def get(self, **kwargs):
return render_response('new_post.html', form=self.form)
@login_required
def post(self, **kwargs):
if self.form.validate():
post = Post(
title = self.form.title.data,
content = self.form.content.data,
author = self.auth_session
)
post.put()
return redirect('/posts')
return self.get(**kwargs)
@cached_property
def form(self):
return PostForm(self.request)
A lot of news here: first, tipfy explores the multi-inheritance Python feature and if you will use the auth extension by the native App Engine users API, you have to create you handler class extending AppEngineAuthMixin and AllSessionMixins classes, and add to the middleware list the SessionMiddleware class. See more at the tipfy docs.
The last step is create the new_post.html template and deploy the application. Here is the new_post.html template code:
{% block title %}
New post
{% endblock %}
{% block content %}
<form action="" method="post" accept-charset="utf-8">
<p>
<label for="title">{{ form.title.label }}</label>
{{ form.title|safe }}
{% if form.title.errors %}
<ul class="errors">
{% for error in form.title.errors %}
<li>{{ error }}</li>
{% endfor %}
</ul>
{% endif %}
</p>
<p>
<label for="content">{{ form.content.label }}</label>
{{ form.content|safe }}
{% if form.content.errors %}
<ul class="errors">
{% for error in form.content.errors %}
<li>{{ error }}</li>
{% endfor %}
</ul>
{% endif %}
</p>
<p><input type="submit" value="Save post"/></p>
</form>
{% endblock %}
Now, we can deploy the application on Google App Engine by simply running this command:
And you can check the deployed application live here: http://4.latest.gaeseries.appspot.com
The code is available at Github: http://github.com/fsouza/gaeseries/tree/tipfy. Enjoy ;)

>What's your opinion on Flask vs tipfy?
Flask seems more simple for me but tipfy gives you more power for appengine, for example the tipfy.auth extention
>I agree :)
I like Flask because I can use Flask in other places, not only App Engine, and tipfy is a framework for App Engine only.
If you are thinking of using only App Engine, tipfy is a good choice =D
>Nice writeup
>Only a subset of extensions (those listed in buildout.cfg) are installed. Instructions to add or remove extensions, such as WTForms:
http://www.tipfy.org/wiki/guide/extensions/#adding-or-removing-extensions
>Great tip, Stephen!
Thanks ;)
>Hey Francisco,
since the upcoming version 0.7 of tipfy, you can use it outside of App Engine also.
Considering this, do you think it's worth learning tipfy when you don't plan to use App Engine or do you think it's better to go with Flask in this case?
Thanks in advance!
>Hey,
I prefer Flask, cause it's is very simple and works fine :)
Hi Francisco, I think your series of AppEngine examples is great. I posted a remix of your example using the Tornado framework (http://j.mp/kF0st8). You are welcome to use it to extend your series if you wish. Again thanks for your posts.
Is this still considered current? Anyone know if there has been much change to tipfy?