Como armazenar dados no Datastore

Esta parte das instruções do código do livro de visitas do Python mostra como armazenar dados estruturados no Datastore. Com o App Engine e o Datastore, você não precisa se preocupar com distribuição, replicação e balanceamento de carga de dados. Isso é feito para você por meio de uma API simples. Além disso, você recebe um poderoso mecanismo de consulta e transações.

Esta página é parte de um tutorial com várias páginas. Para começar e ver as instruções de configuração, acesse Como criar um aplicativo Guestbook.

Como armazenar as saudações enviadas

Os dados são gravados no Datastore em objetos conhecidos como entidades. Cada entidade tem uma chave que a identifica de forma exclusiva. Como alternativa, uma entidade pode designar outra entidade como pai. A primeira entidade é o filho da entidade pai. Assim, no armazenamento de dados, as entidades formam um espaço hierarquicamente estruturado, semelhante à estrutura de diretórios de um sistema de arquivos. Para mais informações, veja Como estruturar dados para consistência forte.

O App Engine inclui uma API de modelagem de dados para Python. Para usar a API de modelagem de dados, importe o módulo google.appengine.ext.ndb. Cada saudação inclui o nome do autor, o conteúdo da mensagem, a data e hora em que a mensagem foi postada. O aplicativo exibe mensagens em ordem cronológica. O código abaixo define o modelo de dados:

class Author(ndb.Model):
    """Sub model for representing an author."""
    identity = ndb.StringProperty(indexed=False)
    email = ndb.StringProperty(indexed=False)

class Greeting(ndb.Model):
    """A main model for representing an individual Guestbook entry."""
    author = ndb.StructuredProperty(Author)
    content = ndb.StringProperty(indexed=False)
    date = ndb.DateTimeProperty(auto_now_add=True)

O código define um modelo de Greeting com três propriedades: author em que o valor é um objeto Author com o endereço de e-mail e a identidade do autor, content, em que o valor é uma string e date em que o valor é um datetime.datetime.

Alguns construtores de propriedades usam parâmetros para configurar ainda mais esse comportamento. Passando o construtor ndb.StringProperty, o parâmetro indexed=False informa que os valores para essa propriedade não serão indexados. Isso economiza gravações desnecessárias porque o aplicativo nunca usa essa propriedade em uma consulta. Passando o construtor ndb.DateTimeProperty um parâmetro auto_now_add=True configura o modelo para que forneça automaticamente a novos objetos um carimbo de datetime do horário em que o objeto foi criado, caso o aplicativo não forneça um valor. Para uma lista completa dos tipos de propriedades e respectivas opções, veja Propriedades do NDB.

O aplicativo usa o modelo de dados para criar novos objetos Greeting e colocá-los no Datastore. O gerenciador do Guestbook cria novas saudações e as salva no armazenamento de dados:

class Guestbook(webapp2.RequestHandler):

    def post(self):
        # We set the same parent key on the 'Greeting' to ensure each
        # Greeting is in the same entity group. Queries across the
        # single entity group will be consistent. However, the write
        # rate to a single entity group should be limited to
        # ~1/second.
        guestbook_name = self.request.get('guestbook_name',
                                          DEFAULT_GUESTBOOK_NAME)
        greeting = Greeting(parent=guestbook_key(guestbook_name))

        if users.get_current_user():
            greeting.author = Author(
                    identity=users.get_current_user().user_id(),
                    email=users.get_current_user().email())

        greeting.content = self.request.get('content')
        greeting.put()

        query_params = {'guestbook_name': guestbook_name}
        self.redirect('/?' + urllib.urlencode(query_params))

Este gerenciador do Guestbook cria um novo objeto Greeting. Em seguida, configura as respectivas propriedades, author e content, com os dados postados pelo usuário. O pai de Greeting é uma entidade Guestbook. Não é necessário configurar a entidade Guestbook antes de configurá-la para ser pai de outra. Neste exemplo, o pai é usado como um marcador para fins de transação e consistência. Para mais informações, veja a página Transações. Objetos que compartilham um ancestral comum pertencem ao mesmo grupo de entidades. O código não configura a propriedade date. Portanto, a date é configurada automaticamente para o presente, usando auto_now_add=True.

Finalmente, greeting.put() salva o novo objeto no armazenamento de dados. Caso tivéssemos adquirido esse objeto de uma consulta, put() teria atualizado o objeto atual. Como criamos esse objeto com o construtor do modelo, put() adiciona o novo objeto ao armazenamento de dados.

Como a consulta no Datastore é altamente consistente apenas nos grupos de entidades, o código atribui todas as saudações de um livro ao mesmo grupo de entidades, configurando o mesmo pai para cada saudação. Isso quer dizer que o usuário sempre vê uma saudação imediatamente depois de ela ser gravada. Entretanto, a taxa com que você grava no mesmo grupo de entidades é limitada a uma gravação para o grupo de entidades por segundo. Tenha isso em mente quando projetar seu aplicativo real. Observe que, ao usar serviços como o Memcache, você diminui a chance de um usuário ver resultados obsoletos ao consultar vários grupos de entidades após uma gravação.

Como recuperar saudações enviadas

O Datastore tem um mecanismo de consulta sofisticado para modelos de dados. Como o Datastore não é um banco de dados relacional tradicional, as consultas não são especificadas usando o SQL. Em vez disso, os dados são consultados de duas maneiras: usando Consultas do armazenamento de dados ou usando uma linguagem de consulta semelhante a SQL chamada GQL . Para acessar toda a gama de recursos de consulta do Datastore, recomendamos o uso de consultas sobre o GQL.

O gerenciador MainPage recupera e exibe saudações enviadas anteriormente. A consulta é executada com a chamada greetings_query.fetch(10).

Mais sobre índices do Datastore

Cada consulta no Datastore é calculada a partir de um ou mais índices: tabelas que mapeiam os valores de propriedade ordenados para chaves de entidade. Dessa forma, o App Engine consegue fornecer resultados rapidamente, seja qual for o tamanho do armazenamento de dados do aplicativo. Muitas consultas são calculadas dos índices integrados, mas para consultas mais complexas, o Datastore requer um índice personalizado. Sem um índice personalizado, o Datastore não pode executar essas consultas de forma eficiente.

Por exemplo, o aplicativo Guestbook filtra por livro de visitas e ordena por data, usando uma consulta de ancestral e uma ordem de classificação. Isso exige que um índice personalizado seja especificado no arquivo index.yamldo seu aplicativo. É possível editar esse arquivo manualmente ou tratar dele de forma automática, executando as consultas no aplicativo localmente. Depois que o índice for criado em index.yaml, a implantação do aplicativo também implanta as informações de índice personalizadas.

A definição da consulta em index.yaml é semelhante a esta:

indexes:
- kind: Greeting
  ancestor: yes
  properties:
  - name: date
    direction: desc

Saiba mais sobre os índices do Datastore na página de Índices do Datastore. Leia sobre a especificação adequada para arquivos index.yaml em Configuração de índice do Datastore em Python.