
A little late, finally I introduce the third part of using Python frameworks in Google App Engine. I wrote before about web2py and Django, and now is the time of Flask, a Python microframework based on Werkzeug, Jinja2 and good intentions. Unlike Django and web2py, Flask is not a full stack framework, it has not a database abstraction layer or an object relational mapper, Flask is totally decoupled from model layer. It is really good, because we can use the power of SQLAlchemy (which is, IMHO, the best ever data abstraction framework =P) when we are working with relational databases, and when work with non-relational databases, we can use the native API.
Flask is a microframework, what means that we have more power on customizing the applications, but it is also a little more painful to build an application, because the framework is not a father that does about 10 billion of things for us: it is simple, but still fun! As Flask has no data abstraction layer, we will use the BigTable API directly.
So, as we have done in other parts of the series, the sample application will be a very simple blog, with a public view listing all posts and other login protected view used for writing posts. The first step is to setup the environment. It is very simple, but I little laborious: first we create an empty directory and put the app.yaml file inside it (yes, we will build everything from scratch =D). Here is the app.yaml code:
version: 3
runtime: python
api_version: 1
handlers:
- url: .*
script: main.py
We just set the application ID, the version and the URL handlers. We will handle all request in main.py file. Late on this post, I will show the main.py module, the script that handles Flask with Google App Engine. Now, let’s create the Flask application, and deal with App Engine later :)
Now we need to install Flask inside the application, so we get the Flask from Github (I used 0.6 version), extract it and inside the flask directory get the flask subdirectory (pay attention here). Because Flask depends on Werkzeug and Jinja2, and Jinja2 depends on simplejson, you need to get these libraries and install in your application too. Here is how to you can “install” everything:
$ unzip mitsuhiko-flask-0.6-0-g5cadd9d.zip
$ cp -r mitsuhiko-flask-5cadd9d/flask ~/Projetos/blog/gaeseries
$ wget http://pypi.python.org/packages/source/W/Werkzeug/Werkzeug-0.6.2.tar.gz
$ tar -xvzf Werkzeug-0.6.2.tar.gz
$ cp -r Werkzeug-0.6.2/werkzeug ~/Projetos/blog/gaeseries/
$ wget http://pypi.python.org/packages/source/J/Jinja2/Jinja2-2.5.tar.gz
$ tar -xvzf Jinja2-2.5.tar.gz
$ cp -r Jinja2-2.5/jinja2 ~/Projetos/blog/gaeseries/
$ wget http://pypi.python.org/packages/source/s/simplejson/simplejson-2.1.1.tar.gz
$ tar -xvzf simplejson-2.1.1.tar.gz
$ cp -r simplejson-2.1.1/simplejson ~/Projetos/blog/gaeseries/
On my computer, the project is under ~/Projetos/blog/gaeseries, put all downloaded tools on the root of your application. Now we have everything that we need to start to create our Flask application, so let’s create a Python package called blog, it will be the application directory:
$ touch blog/__init__.py
Inside the __init__.py module, we will create our Flask application and start to code ;) Here is the __init__.py code:
import settings
app = Flask('blog')
app.config.from_object('blog.settings')
import views
We imported two modules: settings and views. So we should create the two modules, where we will put the application settings and the views of applications (look that Flask deals in the same way that Django, calling “views” functions that receives a request and returns a response, instead of call it “actions” (like web2py). Just create the files:
$ touch blog/settings.py
Here is the settings.py sample code:
SECRET_KEY='dev_key_h8hfne89vm'
CSRF_ENABLED=True
CSRF_SESSION_LKEY='dev_key_h8asSNJ9s9=+'
Now is the time to define the model Post. We will define our models inside the application directory, in a module called models.py:
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)
The last property is a UserProperty, a “foreign key” to a user. We will use the Google App Engine users API, so the datastore API provides this property to establish a relationship between custom models and the Google account model.
We have defined the model, and we can finally start to create the application’s views. Inside the views module, let’s create the public view with all posts, that will be accessed by the URL /posts:
from models import Post
from flask import render_template
@app.route('/posts')
def list_posts():
posts = Post.all()
return render_template('list_posts.html', posts=posts)
On the last line of the view, we called the function render_template, which renders a template =P The first parameter of this function is the template to be rendered, we passed the list_posts.html, so let’s create it using the Jinja2 syntax, inspired by Django templates. Inside the application directory, create a subdirectory called templates and put inside it a HTML file called base.html. That file will be the application layout and here is its code:
<head>
<meta http-equiv="Content-type" content="text/html; charset=utf-8"/>
<title>{% block title %}Blog{% endblock %}</title>
</head>
<body id="">
{% block content %}
{% endblock %}
</body>
</html>
And now create the list_posts.html template, with the following code:
{% block content %}
<ul>
{% for post in posts %}
<li>
{{ post.title }} (written by {{ post.author.nickname() }})
{{ post.content }}
</li>
{% endfor %}
</ul>
{% endblock %}
Now, for test it, we need to run Google App Engine development server on localhost. The app.yaml file defined a main.py script as handler for all requests, so to use Google App Engine local development server, we need to create the main.py file that run our application. Every Flask application is a WSGI application, so we can use an App Engine tool for running WSGI application. In that way, the main.py script is really simple:
from blog import app
run_wsgi_app(app)
The script uses the run_wsgi_app function provided by webapp, the built-in Google Python web framework for App Engine. Now, we can run the application in the same way that we ran in the web2py post:
And if you access the URL http://localhost:8080/posts in your browser, you will see a blank page, just because there is no posts on the database. Now we will create a login protected view to write and save a post on the database. Google App Engine does not provide a decorator for validate when a user is logged, and Flask doesn’t provide it too. So, let’s create a function decorator called login_required and decorate the view new_post with that decorator. I created the decorator inside a decorators.py module and import it inside the views.py module. Here is the decorators.py code:
from google.appengine.api import users
from flask import redirect, request
def login_required(func):
@wraps(func)
def decorated_view(*args, **kwargs):
if not users.get_current_user():
return redirect(users.create_login_url(request.url))
return func(*args, **kwargs)
return decorated_view
In the new_post we will deal with forms. IMHO, WTForms is the best way to deal with forms in Flask. There is a Flask extension called Flask-WTF, and we can install it in our application for easy dealing with forms. Here is how can we install WTForms and Flask-WTF:
$ unzip WTForms-0.6.zip
$ cp -r WTForms-0.6/wtforms ~/Projetos/blog/gaeseries/
$ wget http://pypi.python.org/packages/source/F/Flask-WTF/Flask-WTF-0.2.3.tar.gz
$ tar -xvzf Flask-WTF-0.2.3.tar.gz
$ cp -r Flask-WTF-0.2.3/flaskext ~/Projetos/blog/gaeseries/
Now we have installed WTForms and Flask-WTF, and we can create a new WTForm with two fields: title and content. Remember that the date and author will be filled automatically with the current datetime and current user. Here is the PostForm code (I put it inside the views.py file, but it is possible to put it in a separated forms.py file):
from flaskext.wtf import validators
class PostForm(wtf.Form):
title = wtf.TextField('Title', validators=[validators.Required()])
content = wtf.TextAreaField('Content', validators=[validators.Required()])
Now we can create the new_post view:
@login_required
def new_post():
form = PostForm()
if form.validate_on_submit():
post = Post(title = form.title.data,
content = form.content.data,
author = users.get_current_user())
post.put()
flash('Post saved on database.')
return redirect(url_for('list_posts'))
return render_template('new_post.html', form=form)
Now, everything we need is to build the new_post.html template, here is the code of that template:
{% block content %}
<h1 id="">Write a post</h1>
<form action="{{ url_for('new_post') }}" method="post" accept-charset="utf-8">
{{ form.csrf_token }}
<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 everything is working :) We can run Google App Engine local development server and access the URL http://localhost:8080/posts/new on the browser, then write a post and save it :D Everything is ready to deploy, and the deploy process is the same of web2py, just run on terminal:
And now the application is online :) Check this out: http://3.latest.gaeseries.appspot.com (use your Google Account to write posts).
You can also check the code out in Github: http://github.com/fsouza/gaeseries/tree/flask.
>You could use web2py's DAL to the Model layer! so easy to implement.
Great Post, I will try Flask on GAE
Tks
>Thanks for the tip, Bruno.
I just wanted to show up how to integrate Flask with "native" App Engine datastore.
>In the source of 'new_post.html', inside the second 'p' element, I think it should say
label for="content"
>Thanks Chris, I fixed it :)
>Awesome tutorial – really helpful for starting out with App Engine and Flask.
I'm having trouble getting the interactive debugger to work though – is this possible when using dev_appserver.py?
>No, it is impossible, because the interactive debugger is provided by Werkzeug and when we use dev_appserver.py, we don't use Werkzeug =)
>Does Flask have some kind of form model similar to Django? If so, how does it compare?
>Jay, Flask is not a full stack framework like Django.
If you want to deal with forms, you can use WTForms with the Flask-WTF extension, or FormAlchemy, that integrates well with SQLAlchemy ;)
>Thanks for this Francisco,could you provide some tips on organising packages in flask. For eg:-
all the packages like wtforms or blinker could be put in a helper package , like this:-
-flask
-flaskext
-app
-helper
-blinker
-wtforms
something like site-packages on the local structure.
>Hey Alice,
check this project out on Github, I use the "lib" dir to put my dependencies: http://github.com/franciscosouza/labs
I just add that directory to the path in the main.py script [1].
I hope that I could help you.
[1] http://github.com/franciscosouza/labs/blob/master/main.py
>Thanks Francisco… that was awesome.. been looking for this everywhere!
>Thank you Francisco for the tricky solution, have been looking for that! :-))
Thanks heaps for the tutorial, Francisco! I’m just having one problem. I can’t import the flask-wtf extension without getting an error. The flaskext/__init__.py file throws sm error like this:
<type 'exceptions.ImportError'>: No module named pkg_resources
do you have any idea why this happens? I’d really like to finish my flask web app, and this is the only issue I’ve had. Thanks again!
Hi Tom, I missed this file on tutorial.
You can get it on your Python system installation. On Linux (Ubuntu):
If you didn’t find the file, you can also get it from my repository on Github: https://github.com/franciscosouza/gaeseries/raw/flask/pkg_resources.py
Keep hacking :)
Thanks Francisco. not entirely sure where that file is supposed to be put (i’m on a mac if it makes any difference) but i removed the
line from the __init__.py file and it worked. And on another note, for some reason the validate_on_submit function doesn’t actually validate anything for me. vene if all fields are empty, it’ll try and pass the values to the google db.
I think you shouldn’t remove that code. It code says the Python package flaskext should act as a Python namespace.
You should just copy the pkg_resources.py from your Python installation. Unfortunately, I can’t remember now where is it, but you can get the file on Github repository and put it on the root of the application: https://github.com/franciscosouza/gaeseries/tree/flask
OK, it works now. Thanks for all the help! Also, I fixed my form validation by changing validate_on_submit() to form.validate()
I am looking for a programmer to develop a dating site running on Google Apps Engine. Interested, please contact me at chinesesociopolitics at …………. gmail com
great post! but please move the slider from middle left to somewhere else, your blog is difficult to read in my mac and chrome env. thanks
Hello, thanks for the feedback, I just removed the slider.
I can put it on right side only if I pay the pro version, and I’m not going to pay for it :)
Thanks a lot for this, this got my existing flask app running with the dev server in not_very_many minutes. One note: simplejson is not required, or at least, not by jinja2. It’s not listed as a prereq there, and there’s nothing in the code mentioning it either.
Nice article, thank you.
Hi Francisco. This article is great, but you should do a follow up post about unit testing now. :)
There is very little information about this so I ended up creating a pull request for the flask-appengine-template project, but I believe it could be done in a better way.
https://github.com/jbochi/flask-appengine-template/commit/5dbfa1b257f8e6c25bd9331aa0fd60915ba91875#L3L-1
How are you handling unit tests?
Hi Juarez,
I usually use NoseGAE when testing Google App Engine applications, so it sets up the GAE environment for tests.
Exploring the power of nose for global test hooks, I like to add a setup function in the __init__.py file of tests directory, so the file looks like this:
from blog import app
app.config['TESTING'] = True
app.config['CSRF_ENABLED'] = False
I extracted the code sample above from a real code (closed code, unfortunately). I disabled the CSRF because I was using Flask-WTF.
I need to write more about tests on GAE and Flask, I know :(
I really like it
Thanks, Francisco, for these great posts on the GAE, I have learned a lot. Moving on to tipfy next.
This is a really helpful writeup – thank you!
I’m very excited by what I’ve seen with flask so far, especially after banging my head against GAE’s webapp framework a few times.
A year and a half later, and still relevant. Thanks for the overview, now I’m off to develop my app :)