Criar modelos de entidades

Tem de criar uma classe de modelo para a sua entidade. Pode fazê-lo de duas formas:

  • Crie uma classe de modelo que defina as propriedades da entidade.
  • Crie uma classe de modelo expansível que não defina entidades antecipadamente.

Este documento descreve como criar cada um destes tipos de classes de modelos. Também descreve como criar um gancho de modelo para que a sua aplicação possa executar algum código antes ou depois de algum tipo de operações, por exemplo, antes de cada get().

Criar uma classe de modelo com propriedades

Antes de criar uma entidade, tem de criar uma classe de modelo que defina uma ou mais propriedades de entidade. Por exemplo:

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

em que quer que cada entidade Account tenha propriedades para o nome de utilizador, o ID do utilizador e o email.

Para ver uma lista completa dos tipos de propriedades, consulte a referência de propriedades de entidades.

Criar uma classe de modelo Expando

Não tem de usar uma classe de modelo que defina propriedades antecipadamente. Uma subclasse de modelo especial denominada Expando altera o comportamento das respetivas entidades para que qualquer atributo atribuído seja guardado no repositório de dados. Tenha em atenção que estes 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()

Isto escreve uma entidade no armazenamento de dados com uma propriedade foo com o valor inteiro 1, uma propriedade bar com o valor de string 'blah' e uma propriedade tag repetida com os valores de string 'exp', 'and' e 'oh'. As propriedades são indexadas e pode inspecioná-las através do atributo _properties da entidade:

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

Um Expando criado através da obtenção de um valor do armazenamento de dados tem propriedades para todos os valores de propriedades que foram guardados no armazenamento de dados.

Uma aplicação pode adicionar propriedades predefinidas a uma subclasse Expando:

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

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

Para criar uma subclasse Expando cujas propriedades não estão indexadas, defina _default_indexed = False na definição da 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)
# }

Também pode definir _default_indexed numa entidade Expando. Neste caso, afeta todas as propriedades atribuídas depois de ter sido definida.

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

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

não funciona, porque a classe não tem um objeto de propriedade para a propriedade de localização. Em alternativa, use GenericProperty, a classe que Expando usa para propriedades dinâmicas:

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

Usar hooks de modelos

O NDB oferece um mecanismo de associação simples. Ao definir um gancho, uma aplicação pode executar algum código antes ou depois de algum tipo de operações; por exemplo, um Model pode executar alguma função antes de cada get().

Uma função de gancho é executada quando usa as versões síncronas, assíncronas e múltiplas do método adequado. Por exemplo, um gancho "pre-get" aplicar-se-ia a todos os get(), get_async() e get_multi(). Existem versões pré-RPC e pós-RPC de cada gancho.

Os hooks podem ser úteis para o seguinte:

  • Colocação em cache de consultas
  • Auditoria da atividade do Cloud Datastore por utilizador
  • Imitar acionadores de base de dados

O exemplo seguinte 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 usar post-hooks com APIs assíncronas, os hooks são acionados chamando check_result(), get_result() ou produzindo (num tasklet) o futuro de um método assíncrono. Os post hooks não verificam se o RPC foi bem-sucedido; o hook é executado independentemente da falha.

Todos os post-hooks têm um argumento Future no final da assinatura da chamada. Este objeto Future contém o resultado da ação. Pode chamar get_result() neste Future para obter o resultado. Pode ter a certeza de que get_result() não vai bloquear, uma vez que o Future está concluído quando o gancho é chamado.

Gerar uma exceção durante um pré-gancho impede que o pedido seja realizado. Embora os hooks sejam acionados nos métodos <var>*</var>_async, não pode antecipar um RPC ao gerar tasklets.Return num hook pré-RPC.

O que se segue?