Google App Engine e Datastore

Sumário

Em um post anterior, vimos como manipular no servidor os dados vindos de um formulário HTML, com Google App Engine (GAE) e Python. Porém, até agora não vimos como armazenar dados vindos do cliente em um banco de dados.

Neste post vamos incrementar a aplicação de envio de mensagens via formulários HTML que construímos em um post anterior, possibilitando que as mensagens enviadas pelos usuários sejam gravadas em um banco de dados e que sejam listadas logo abaixo do formulário na página principal. Vamos começar vendo o mecanismo para armazenamento de dados do GAE: o datastore.

Datastore

Diferentemente do que acontece quando estamos lidando diretamente com um banco de dados relacional, onde criamos as tabelas com suas propriedades no banco de dados, para então ajeitar a aplicação para que possa acessar tal banco, com o GAE nós poderemos implementar uma app inteira sem ver código SQL algum.

Para criar uma entidade no BD, basta definir uma classe contendo os atributos que desejamos que façam parte de nosso BD. Vamos criar uma entidade chamada de Mensagem, que irá armazenar os atributos relacionados às mensagens enviadas pelo usuário:

from google.appengine.ext import db

class Mensagem(db.Model):
    nome_usuario = db.StringProperty(required=True)
    url = db.LinkProperty()
    email = db.EmailProperty()
    comentario = db.TextProperty(required=True)

Nossa classe estende a classe db.Model, o que é um requisito para que ela possa representar uma entidade no BD. Veja que a definição de uma entidade é bem simples, bastando declarar os atributos desejados como variáveis de classe. Já definimos também os tipos dos dados que cada atributo irá representar. No exemplo acima, StringProperty é o tipo que usamos para definição de campos de texto com menos de 500 caracteres. Já o tipo TextProperty é usado para a definição de campos para textos mais longos. Para conhecer a lista completa dos tipos de atributos disponíveis no framework de persistência do Google App Engine, consulte a documentação oficial.

Persistência de objetos no datastore

Uma vez que definimos uma entidade para representação de nossas mensagens no banco de dados, criar e salvar dados no BD é tão simples quanto criar um objeto:

msg = Mensagem()
msg.nome_usuario = "stummjr"
msg.url = "https://pythonhelp.wordpress.com"
msg.email = "stummjr@someemail.com"
msg.comentario = "Um comentário bem interessante!!!"
msg.put()

O procedimento é bem simples. Primeiramente, instanciamos um objeto da classe Mensagem (linha 1). Após isso, setamos os atributos desse objeto (linhas 2-5). Por fim, salvamos tal objeto no BD (linha 6).

Também poderíamos passar os valores para todos os atributos como parâmetros nomeados na construção do objeto:

msg = Mensagem(
    nome_usuario="stummjr",
    url="https://pythonhelp.wordpress.com",
    email="stummjr@someemail.com",
    comentario="Um comentário bem interessante!!!"
)
msg.put()

Agora já podemos integrar o código acima em nossa app do post anterior. Basta criar o objeto Mensagem e persistir esse objeto no BD dentro do método post, que irá tratar os dados enviados pelo usuário via formulário:

def post(self):
    msg = Mensagem(
        nome_usuario=self.request.get('nome_usuario'),
        url=self.request.get('url'),
        email=self.request.get('email'),
        comentario=self.request.get('comentario')
    )
    msg.put()
    self.redirect('/')

Observe que, ao final do método post(), fizemos uma chamada ao método self.redirect('/'), que faz com que o usuário seja redirecionado para o recurso recebido como parâmetro (nesse caso, a raiz do site). Fizemos isso porque se apenas enviarmos ao usuário uma resposta de confirmação, ele poderá causar a submissão duplicada do formulário caso solicite ao navegador que este atualize a página (o famoso F5). Assim, redirecionamos a outro recurso para evitar que uma sequência de refreshes na página recebida como resposta a um POST possa causar várias inserções duplicadas.

Adicionamos os campos nome_usuario, url e email ao formulário contido na variável html:

html = """
    <html>
        <head>
            <title></title>
        </head>
        <body>
            <form method="POST" action="/">
                Nome: <p><input type="text" name="nome_usuario"></p>
                URL: <p><input type="text" name="url"></p>
                Email: <p><input type="text" name="email"></p>
                Comentário:<p><textarea name="comentario" cols="40" rows="10"></textarea></p>
                <button type="submit">Enviar</button>
            </form>
        </body>
    </html>
"""

Feito isso, agora nosso app já é capaz de salvar no BD os comentários enviados pelos usuários. Caso já tenha visto o post anterior, apenas complemente aquele código com o que foi apresentado até aqui e execute servidor de desevolvimento local. Se não viu o post anterior, o código completo é apresentado na seção “O projeto completo”.

A interface administrativa local

Enquanto estamos desenvolvendo nosso app, podemos querer verificar se determinado objeto foi realmente persistido no BD ou não. Caso ainda não tenhamos desenvolvido algum método para listagem de objetos em nosso app, podemos utilizar a interface administrativa local do GAE, que fica disponível na URL http://localhost:8080/_ah/admin/ (obs.: o servidor de testes precisa estar em execução)

http7.1

Basta selecionar o tipo de objeto e solicitar a listagem.

Consultas ao datastore

Para realizar consultas aos objetos que estão no banco de dados, basta utilizar os métodos all(), filter(), dentre outros que são comuns a todas as classes que estendem db.Model. Veja alguns exemplos:

# obtém todos os registros de Mensagem do BD
msgs = Mensagem.all()

# pega somente Mensagens do "John Doe"
msgs.filter('nome_usuario =', "John Doe")

# ordena pelo endereço do email, de forma DESC (-)
msgs.order('-email')

for msg in msgs:
    print msg.email

Veja mais métodos para consulta de dados na documentação oficial.

Em nosso projeto, precisamos apenas adicionar ao método get() a funcionalidade de listagem das mensagens já existentes no BD, logo abaixo do formulário de submissão de mensagens. Com base nos métodos de consulta que vimos, podemos montar nosso método:

def get(self):
    self.response.out.write(html)
    self.response.out.write('<ul>')
    for msg in Mensagem.all():
        self.response.out.write('<li>' + unicode(msg.comentario) + '</li>')
    self.response.out.write('</ul>')

Tenha em mente que imprimir código HTML diretamente não é uma boa abordagem para desenvolvimento web. Existem técnicas mais recomendadas, como a utilização de modelos de páginas. Mas isso é um assunto para um outro post.

O projeto completo

Como já vimos anteriormente, a estrutura do diretório do nosso projeto deve ficar como:


projeto/
  handlers.py
  app.yaml

Segue abaixo o código dos arquivos relacionados:

Arquivo handlers.py:

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

html = """
    <html>
        <head>
            <title></title>
        </head>
        <body>
            <form method="POST" action="/">
                Nome: <p><input type="text" name="nome_usuario"></p>
                URL: <p><input type="text" name="url"></p>
                Email: <p><input type="text" name="email"></p>
                Comentário:<p><textarea name="comentario" cols="40" rows="10"></textarea></p>
                <button type="submit">Enviar</button>
            </form>
        </body>
    </html>
"""

class PostHandler(webapp2.RequestHandler):

    def get(self):
        self.response.out.write(html)
        self.response.out.write('<ul>')
        for msg in Mensagem.all():
            self.response.out.write('<li>' + unicode(msg.comentario) + '</li>')
        self.response.out.write('</ul>')

    def post(self):
        msg = Mensagem(
            nome_usuario=self.request.get('nome_usuario'),
            url=self.request.get('url'),
            email=self.request.get('email'),
            comentario=self.request.get('comentario')
        )
        msg.put()
        self.redirect('/')


class Mensagem(db.Model):
    nome_usuario = db.StringProperty(required=True)
    url = db.LinkProperty()
    email = db.EmailProperty()
    comentario = db.TextProperty(required=True)


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

Arquivo app.yaml:


application: gaepost
version: 1
runtime: python
api_version: 1

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

Com os arquivos acima, você já pode executar o projeto usando o ambiente de testes.

Aprenda mais

Caso tenha dificuldades para entender o funcionamento do Google App Engine, leia os posts anteriores nos quais introduzimos o assunto:

  1. Desenvolvendo com Python e Google App Engine
  2. Tratando dados de formulários com Google App Engine

Se quiser ir mais a fundo no GAE, sugiro que leia a documentação oficial, e também o livro Programming Google App Engine.

Anúncios

4 comentários sobre “Google App Engine e Datastore

  1. Pingback: Templates de páginas com Python e Google App Engine | Python Help
  2. Pingback: Programming Google App Engine | WWW.MPROREVIEW.COM
  3. Pingback: Memcache 101 | Python Help
  4. Pingback: Um blog com Google App Engine | Python Help

Deixe um comentário

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair / Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair / Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair / Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair / Alterar )

Conectando a %s