Francisco Souza

Open source, Python, Django, Java, agile e outras coisas mais

Voando Com O Flask No Google App Engine

FlaskDepois de uma pequena pausa, chegamos finalmente à terceira parte da série de posts sobre o uso de frameworks Python no Google App Engine. Após abordar o uso do Django e do web2py no App Engine, agora veremos como usar o Flask, um microframework para Python baseado no Werkzeug, no Jinja2 e em boas intenções. Diferente do Django e do web2py, o Flask não possui uma camada de abstração do banco de dados, não é um framework full stack. Trata-se de uma excelente decisão de design, uma vez que é possível trabalhar com o SQLAlchemy (ou qualquer outro ORM) em sistemas que usam bancos de dados relacionais, e a API nativa para os bancos de dados não relacionais, quando for o caso.

O fato de o Flask ser um microframework significa que há mais espaço para customização e menos acoplamento, porém é um pouco mais trabalhoso (mas não difícil) construir uma aplicação com Flask, pois há muitos blocos a serem montados, e o Flask não faz 10 bilhões de coisas pelo desenvolvedor. Apesar disso tudo, desenvolver com Flask é prazeroso e divertido! Como o Flask não conta com uma camada de abstração de dados nem um ORM, vamos utilizar a API nativa de armazenamento de dados do Google App Engine.

Como foi feito nas duas primeiras partes, vamos criar um blog versão mini onde teremos duas views: uma para listar os posts, de acesso público, e uma para escrever posts, com acesso restrito por login. O primeiro passo, como nos outros casos, é configurar o ambiente de desenvolvimento. É algo simples, mas da um pouco de trabalho :) Primeiro, é necessário criar o diretório da aplicação e colocar lá dentro o arquivo app.yaml do Google App Engine, com o seguinte conteúdo:

[cc lang=”yaml”]application: gaeseries version: 3 runtime: python api_version: 1

handlers: - url: .* script: main.py[/cc]

Apenas definimos o ID da aplicação, a versão e um handler para todas as requisições: um script chamado main.py, que definiremos mais adiante. Vamos primeiro criar a aplicação do Flask, e depois lidar com o Google App Engine.

Depois de configurar o app.yaml, vamos instalar o Flask e suas dependências (Werkzeug, Jinja2 e simplejson) dentro da nossa aplicação. Basta baixar as bibliotecas e adicionar na raiz do projeto. Eis abaixo os comandos necessários num Linux/Mac para instalar as ferramentas citadas acima:

[cc lang=”bash”]$ wget http://github.com/mitsuhiko/flask/zipball/0.6 $ 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/[/cc]

Note que, no meu caso, o projeto está dentro de ~/Projetos/blog/gaeseries, por isso copiei tudo que era necessário para este diretório. Agora temos tudo que precisamos para começar a codificar nossa aplicação. Para isso, vamos criar um pacote chamado blog:

[cc lang=”bash”]$ mkdir blog $ touch blog/init.py[/cc]

Adicionamos então ao init.py o código que define a nossa aplicação, nada muito complexo, digno de um microframework:

[cc lang=”python”]from flask import Flask import settings

app = Flask(‘blog’) app.config.from_object(‘blog.settings’)

import views[/cc]

Importamos dois módulos: settings e views. Estes módulos irão conter algumas configurações da aplicação e as views da aplicação, respectivamente. Vamos usar novamente o nosso amigo touch para criar os arquivos:

[cc lang=”bash”]$ touch blog/settings.py $ touch blog/views.py[/cc]

Aqui está um exemplo de código para o arquivo settings.py:

[cc lang=”python”]DEBUG=True SECRET_KEY=’dev_key_h8hfne89vm’ CSRF_ENABLED=True CSRF_SESSION_LKEY=’dev_key_h8asSNJ9s9=+’[/cc]

Bom, agora podemos finalmente criar nosso único modelo, o modelo Post. Dentro do diretório da aplicação, vamos criar um arquivo chamado models.py, onde ficarão armazenados nossos modelos. Eis abaixo o código do modelo:

[cc lang=”python”]from google.appengine.ext import db

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)[/cc]

A última propriedade é uma instância UserProperty, uma “chave estrangeira” para um usuário, um objeto fornecido pela API de usuários do Google App Engine. Nosso modelo já está pronto, e podemos começar as criar as views da aplicação, onde vamos lidar com as requisições do usuário. Primeiro, vamos criar a view de listagem dos posts, que vai ser acessada na URL /posts, dentro do arquivo views.py:

[cc lang=”python”]from blog import app 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)[/cc]

Na última linha da view, foi feita uma chamada à função render_template, fornecida pelo Flask. O primeiro parâmetro da função é uma string contendo o nome do template a ser renderizado. Vamos então criar este template para que a listagem funcione. Para isto, é necessário criar um diretório chamado templates dentro do diretório da aplicação, dentro deste diretório, foi criado um arquivo base.html, que define o layout dos templates. O código do arquivo base.html pode ser visto aqui: http://gist.github.com/519845.

Podemos então criar o template list_posts.html, que herda de base.html. O código deste template está aqui: http://gist.github.com/519846.

Para testar a view que acabamos de criar, precisamos executar o servidor de desenvolvimento do Google App Engine localmente, que lê o arquivo app.yaml. Assim, precisamos agora criar o script main.py para executar nossa aplicação do Flask. Toda aplicação Flask é também uma aplicação WSGI. Podemos utilizar então a biblioteca padrão do Google App Engine para executar aplicações WSGI:

[cc lang=”python”]from google.appengine.ext.webapp.util import run_wsgi_app from blog import app

run_wsgi_app(app)[/cc]

Agora nossa aplicação está pronta para ser executada, e podemos fazer isso da mesma forma que fizemos no post sobre web2py, executando o script dev_appserver.py provido no SDK do Google App Engine:

$ /usr/local/google_appengine/dev_appserver.py .

Agora é possível acessar a listagem de posts na URL http://localhost:8080/posts, o único problema é que não há nenhum post ainda para ser listado :) Então vamos criar a view onde será possível escrever posts, esta view será protegida por login. Logo, para restringir o acesso a esta view, podemos usar um decorator. O único problema aqui é que nem o Flask nem o App Engine fornecem um decorator bacanudo para restringir o acesso à view new_post. O que fazer? Vamos criar um decorator, e colocá-lo dentro de um módulo decorators.py e importar este decorator no módulo views.py. Aqui está o código do módulo decorators.py:

[cc lang=”python”]from functools import wraps 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[/cc]

Na view new_post vamos trabalhar com formulários, e para isto também precisamos de outra biblioteca. Uma biblioteca muito boa é o WTForms (que eu apresentei aqui certa vez, no post de integração entre WTForms e Pylons). É possível integrar o WTForms com o Flask utilizando a extensão Flask-WTF. Novamente, precisamos instalar na raiz do nosso projeto as duas ferramentas, da seguinte forma:

$ wget http://pypi.python.org/packages/source/W/WTForms/WTForms-0.6.zip $ 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/

Agora sim, com o WTForms e o Flask-WTF instalados, podemos construir a classe de formulário e depois, finalmente, a view new_post e o template que exibirá o formulário. Eu coloquei a definição do formulário dentro do módulo views.py, mas em caso de aplicações maiores, o ideal é por os formulários dentro de um ou mais módulos separados. Aqui está a definição do formulário:

[cc lang=”python”]from flaskext import wtf from flaskext.wtf import validators

class PostForm(wtf.Form):

title = wtf.TextField('Title', validators=[validators.Required()])
content = wtf.TextAreaField('Content', validators=[validators.Required()])[/cc]

E, finalmente (antes tarde do que nunca =P), podemos criar a nossa view new_post:

[cc lang=”python”]@app.route(‘/posts/new’, methods = [‘GET’, ‘POST’]) @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)[/cc]

E aqui está o template new_post.html: http://gist.github.com/520110. Agora nossa aplicação está pronta: é necessário efetuar login para escrever posts, e a listagem de posts está disponível publicamente.

Você pode acessar a aplicação online neste endereço: http://3.latest.gaeseries.appspot.com (utilize sua conta do Google para efetuar login e escrever posts).

O código completo da aplicação está disponível no Github: http://github.com/fsouza/gaeseries/tree/flask.