Como criar modelos de entidade

Você precisa criar uma classe de modelo para a entidade. Faça isso de duas maneiras:

  • Crie uma classe de modelo que defina propriedades de entidade.
  • Crie uma classe de modelo expando que não defina entidades antecipadamente.

Veja neste documento como criar cada um desses tipos de classes de modelo. Você também aprenderá a criar um gancho de modelo. Dessa maneira, o aplicativo pode executar um código antes ou depois de algum tipo de operações. Por exemplo, antes de cada get().

Como criar uma classe de modelo com propriedades

Para criar uma entidade, você precisa criar uma classe de modelo que defina uma ou mais propriedades de entidade. Exemplo:

from google.appengine.ext import ndb
...
class Account(ndb.Model):
    username = ndb.StringProperty()
    userid = ndb.IntegerProperty()
    email = ndb.StringProperty()

É conveniente que cada entidade Account tenha propriedades para nome de usuário, código de usuário e e-mail.

Para uma lista completa de tipos de propriedade, consulte Referência da propriedade Entity.

Como criar uma classe de modelo Expando

Não é preciso usar uma classe de modelo que defina propriedades com antecedência. Uma subclasse de modelo especial chamada Expando altera o comportamento das entidades. Dessa maneira, qualquer atributo atribuído é salvo no armazenamento de dados. Esses atributos não podem começar com um sublinhado.

Veja como criar um modelo Expando:

class Mine(ndb.Expando):
    pass
...
e = Mine()
e.foo = 1
e.bar = 'blah'
e.tags = ['exp', 'and', 'oh']
e.put()

Isso grava uma entidade no armazenamento de dados com uma propriedade foo com valor inteiro 1, uma propriedade bar com valor de string 'blah' e uma propriedade tag repetida com valores de string 'exp', 'and' e 'oh'. As propriedades são indexadas, e é possível inspecioná-las usando o atributo _properties da entidade:

return e._properties
# {
#     'foo': GenericProperty('foo'),
#     'bar': GenericProperty('bar'),
#     'tags': GenericProperty('tags', repeated=True)
# }

Um Expando criado por meio da recuperação de um valor do armazenamento de dados tem propriedades para todos os valores de propriedade que foram salvos no armazenamento de dados.

Um aplicativo pode adicionar propriedades predefinidas a uma subclasse Expando:

class FlexEmployee(ndb.Expando):
    name = ndb.StringProperty()
    age = ndb.IntegerProperty()
...
employee = FlexEmployee(name='Sandy', location='SF')

Isso dá a employee um atributo name com valor 'Sandy', um atributo age com valor None e um atributo dinâmico location com valor 'SF'.

Para criar uma subclasse Expando com propriedades que sejam desindexadas, estabeleça _default_indexed = False na definição de subclasse:

class Specialized(ndb.Expando):
    _default_indexed = False
...
e = Specialized(foo='a', bar=['b'])
return e._properties
# {
#     'foo': GenericProperty('foo', indexed=False),
#     'bar': GenericProperty('bar', indexed=False, repeated=True)
# }

É possível também definir _default_indexed em uma entidade Expando. Nesse caso, isso afetará todas as propriedades atribuídas depois da definição.

Outra técnica útil é consultar um tipo Expando para uma propriedade dinâmica. Uma consulta semelhante à seguinte

FlexEmployee.query(FlexEmployee.location == 'SF')

não funcionará, porque a classe não tem um objeto de propriedade para a propriedade location. Em vez disso, use GenericProperty, a classe Expando usa em propriedades dinâmicas:

FlexEmployee.query(ndb.GenericProperty('location') == 'SF')

Como usar ganchos

O NDB oferece um mecanismo de gancho leve. Ao definir um gancho, um aplicativo pode executar um código antes ou depois de algum tipo de operação. Por exemplo, um Model pode executar uma função antes de cada get().

Uma função de gancho é executada ao usar as versões síncrona, assíncrona e múltipla do método apropriado. Por exemplo, um gancho "pré-get" se aplicaria a get(), get_async() e get_multi(). Existem versões pré-RPC e pós-RPC de cada gancho.

Os ganchos podem ser úteis nestas tarefas:

  • Armazenamento de consultas em cache
  • Auditoria de atividade do Cloud Datastore por usuário
  • Simulação de gatilhos de banco de dados

O exemplo a seguir mostra como definir funções de gancho:

from google.appengine.ext import ndb
...
class Friend(ndb.Model):
    name = ndb.StringProperty()

    def _pre_put_hook(self):
        _notify('Gee wiz I have a new friend!')

    @classmethod
    def _post_delete_hook(cls, key, future):
        _notify('I have found occasion to rethink our friendship.')
...
f = Friend()
f.name = 'Carole King'
f.put()  # _pre_put_hook is called
fut = f.key.delete_async()  # _post_delete_hook not yet called
fut.get_result()  # _post_delete_hook is called

Se você usar pós-ganchos com APIs assíncronas, os ganchos serão acionados chamando check_result(), get_result() ou produzindo (dentro de um tasklet) o futuro de um método assíncrono. Os pós-ganchos não verificam se o RPC foi bem-sucedido. O gancho é executado independentemente da falha.

Todos os pós-ganchos têm um argumento Future ao final da assinatura da chamada. Esse objeto Future mantém o resultado da ação. Chame get_result() neste Future para recuperar o resultado. Verifique se get_result() não será bloqueado, porque Future está completo no momento em que o gancho é chamado.

Gerar uma exceção durante um pré-gancho impede que a solicitação ocorra. Os ganchos são acionados dentro de métodos <var>*</var>_async, mas não é possível antecipar um RPC gerando tasklets.Return em um gancho pré-RPC.

A seguir