Tratando dados de formulários com Google App Engine

http6.0Os formulários HTML são uma das formas mais usadas para passagem de dados dos usuários para os servidores na web. Você, usuário, quantas vezes por dia preenche algum formulário e clica em um botãozinho próximo a ele? Se você faz uma busca no google.com, você preenche um campo de entrada de um formulário e clica no botão “Buscar”. Quando envia um tweet ou uma mensagem no Facebook, você preenche campos de entrada de um formulário e os submete a um servidor. Quando faz login no GMail, você preenche dois campos de entrada (usuário e senha) e então clica no botão “Login” para submeter os dados ao servidor para que este verifique suas credenciais. Ou seja, você usa formulários pra caramba!

Neste post rápido daremos sequência ao post anterior, vendo como implementar na prática o tratamento de dados vindos de formulários em um app web usando Python e Google App Engine (GAE). (caso você não conheça o Google App Engine, leia antes o post anterior)

POSTando dados na web

O objetivo é criar uma aplicaçãozinha web bem simples na qual o usuário possa escrever uma mensagem em um formulário e receber de volta essa mensagem no navegador. Vamos criar esse app do mesmo modo que fizemos no post anterior. Crie um diretório mural e dentro dele crie os arquivos app.yaml (que vai conter as configurações) e handlers.py (que vai conter o código Python para manipular as requisições).

app.yaml:

application: mural
version: 1
runtime: python
api_version: 1

handlers:
- url: /.*
  script: handlers.py

O código acima apresentado é o conteúdo do arquivo de configurações app.yaml, que define alguns parâmetros para o projeto.
O manipulador de requisições (handlers.py) deverá ser implementado da seguinte forma:

  1. Ao receber uma requisição GET, este deve retornar um HTML contendo um formulário para que o usuário escreva sua mensagen;
  2. Ao receber uma requisição POST (vinda do formulário recebido pelo usuário quando executa o GET), este deve apresentar uma página HTML contendo o texto submetido pelo usuário.

Para simplificar a nossa implementação, vamos colocar o seguinte código HTML dentro de uma string no nosso código Python:

<html>
    <head><title></title></head>
    <body>
        <form method="POST" action="/">
            <textarea name="comentario" cols="40" rows="10">
            </textarea>
            <button type="submit">Enviar</button>
        </form>
    </body>
</html>

Faremos isso para que, ao receber uma requisição do tipo GET, nosso appzinho possa simplesmente gravar em self.response.out a string contendo o código acima, fazendo com o formulário HTML seja passado como resposta à requisição GET do cliente. (você já sabe, do post anterior, que ao escrever um valor qualquer em self.response.out estamos adicionando esse valor ao corpo da mensagem de resposta)

Segue abaixo o código de nosso app:

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


html = """
<html>
    <head><title></title></head>
    <body>
        <form method="POST" action="/">
            <textarea name="comentario" cols="40" rows="10">
            </textarea>
            <button type="submit">Enviar</button>
        </form>
    </body>
</html>
"""

class PostHandler(webapp2.RequestHandler):
    def getself):
        self.response.out.write(html)

    def post(self):
        comentario = self.request.get('comentario')
        self.response.out.write(comentario)


mapeamento = [
    ('/', PostHandler),
]
app = webapp2.WSGIApplication(mapeamento)
run_wsgi_app(app)

Observe que a escrita do formulário HTML como resposta ao cliente ocorre dentro do método get(), que é chamado pelo ambiente de suporte sempre que houver uma requisição GET para /. No form temos dois atributos:

  1. method="POST": indica o método HTTP a ser usado para transmitir os dados ao servidor quando o usuário clicar no botão Enviar;
  2. action="/": indica para qual recurso serão enviados os dados do form quando o usuário submeter as informações clicando no botão.

Ou seja, após preencher o campo comentario e clicar no botão Enviar, o usuário faz com que o navegador envie uma mensagem HTTP POST ao recurso / do servidor. Essa mensagem contém, além do cabeçalho, a variável comentario com o conteúdo escrito pelo usuário nesse campo.

Como todas as requisições ao recurso / são tratadas por instâncias de PostHandler (veja a variável mapeamento), definimos em PostHandler um método chamado post() para lidar com requisições HTTP POST a esse recurso. Assim como ocorre para o método get(), o framework que estamos usando (webapp2) define que requisições do tipo POST serão tratadas por um método chamado post() implementado na classe responsável por manipular as requisições.

Sabendo disso, agora observe nosso método post() (chamado pelo ambiente de suporte quando for recebida uma requisição POST).

def post(self):
    comentario = self.request.get('comentario')
    self.response.out.write(comentario)

Veja que temos um atributo self.request que contém os campos enviados pelo usuário e que os valores desses campos podem ser acessados através de self.request.get('nome_do_campo').

Para simplificar nosso app, estamos apenas ecoando a mensagem enviada pelo usuário, de forma que após clicar em Enviar, este receba de volta a mensagem que enviou ao servidor (self.response.out.write(comentario)).

Agora, vamos testar o nosso app.

Testando o app

Para testar o app recém desenvolvido, siga os passos descritos no post anterior, trocando apenas o nome do diretório para o nome do diretório do projeto que criamos neste post.

GET x POST

No código do nosso appzinho, o manipulador trata dois tipos de requisições HTTP: GET e POST. Vamos ver rapidamente as diferenças entre elas.

GET

Método usado geralmente para envio de requisições que não resultem em alterações no servidor. Como o nome já diz, é indicado para a obtenção de dados.

Os parâmetros são passados para o servidor através da própria URL:

http://www.google.com/search?q=teste

No exemplo acima, passamos a variável q com o valor teste para o recurso /search, de forma que o servidor da Google vai acessar essa variável e realizar a busca usando o valor dela como filtro.

POST

Método usado quando a requisição causa alterações no servidor, como em uma requisição que envia dados a serem gravados num banco de dados. Diferentemente do método GET, quando usamos o POST em uma requisição HTTP, os parâmetros não são codificados na URL, mas sim no corpo da mensagem HTTP a ser enviada ao servidor.

Atenção

Deve ficar claro que a forma como armazenamos o conteúdo HTML do formulário (em uma variável no código) não é a melhor solução. Fizemos isso com a única finalidade de simplificar nosso exemplo. Em projetos “de verdade”, normalmente usamos um mecanismo para renderização de templates, que vem incluso com a maioria dos frameworks web. Se quiser saber mais, veja a documentação oficial do GAE sobre templates.

Sabendo como tratar dados recebidos do usuário e como manipular requisições GET e POST, você já pode começar a fazer projetinhos maiores. Vá em frente! 🙂

bPython, um shell “tunado”

Em outros posts, comentei sobre o Dreampie e sobre o iPython, ambos programas para interpretação interativa de código Python. Agora chegou a vez de outra alternativa bem legal, o bPython. O bPython é bem diferente das outras opções, começando por seu estilão ncurses de mostrar as informações.

bpython

bPython completando função

Como mostra o screenshot acima, o bPython, além de completar os nomes de funções e módulos, também mostra o texto de ajuda envolto por uma caixinha, que some assim que não for mais necessária. Isso é muito mais prático do que, por exemplo, antes de chamar uma função, executar help(função) para descobrir o que faz a função e quais são seus argumentos. Além desses recursos de ajuda, o bPython oferece diversos outros recursos, que irei sumarizar a seguir:

  • rewind: permite “voltar no tempo”. Quando executado, esse recurso reavalia a última linha.
  • show source: quando F2 é pressionado após o nome de um módulo, o bPython busca e mostra o código-fonte daquele módulo.
  • pastebin: envia o código digitado pelo usuário ao pastebin, um serviço na web para armazenamento de trechos de código.
  • Destaque de sintaxe: assim como os outros programas apresentados, o bPython também colore a sintaxe do código, a medida que vai sendo digitado.
  • Salvar código: permite salvar o código digitado no shell em um arquivo no disco.
  • Auto-indentação: o bPython indenta automaticamente a próxima linha quando, por exemplo, escrevemos um cabeçalho de função, de loop for, etc.
  • Completação de nomes de arquivos: em diversas situações, é preciso que utilizemos o caminho de arquivos do disco no código Python, como por exemplo, quando vamos abrir um arquivo com a função open. O bPython completa e mostra as opções de arquivos do disco, à medida que digitamos o caminho do arquivo.

Comparado ao iPython, o bPython possui menos recursos. Mas, para quem não vai utilizar os recursos avançados que o iPython oferece, o bPython é uma excelente alternativa. Na página oficial do projeto, existe uma página que apresenta as alternativas ao bPython e, inclusive, deixa claro que os “concorrentes” podem ser mais adequados para determinados usuários. Mais um exemplo legal em um projeto de software livre.

Mais screenshots e screencasts, veja: http://bpython-interpreter.org/screenshots