Crear modelos de entidad

Debes crear una clase de modelo para tu entidad. Puedes hacerlo de dos formas:

  • Crea una clase de modelo que defina las propiedades de la entidad.
  • Crea una clase de modelo expansible que no defina entidades por adelantado.

En este documento se describe cómo crear cada uno de estos tipos de clases de modelo. También se describe cómo crear un enlace de modelo para que tu aplicación pueda ejecutar código antes o después de cierto tipo de operaciones, por ejemplo, antes de cada get().

Crear una clase de modelo con propiedades

Antes de crear una entidad, debes crear una clase de modelo que defina una o varias propiedades de la entidad. Por ejemplo:

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

donde quieres que cada entidad Account tenga propiedades para el nombre de usuario, el ID de usuario y el correo electrónico.

Para ver una lista completa de los tipos de propiedades, consulta la referencia de propiedades de entidad.

Crear una clase de modelo Expando

No es necesario que uses una clase de modelo que defina las propiedades de antemano. Una subclase de modelo especial llamada Expando cambia el comportamiento de sus entidades para que cualquier atributo asignado se guarde en el almacén de datos. Ten en cuenta que estos atributos no pueden empezar por un guion bajo.

A continuación, te indicamos cómo crear un modelo Expando:

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

De esta forma, se escribe una entidad en el almacén de datos con una propiedad foo con el valor entero 1, una propiedad bar con el valor de cadena 'blah' y una propiedad tag repetida con los valores de cadena 'exp', 'and' y 'oh'. Las propiedades están indexadas y puedes inspeccionarlas con el atributo _properties de la entidad:

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

Un Expando creado al obtener un valor del almacén de datos tiene propiedades para todos los valores de propiedad que se hayan guardado en el almacén de datos.

Una aplicación puede añadir propiedades predefinidas a una subclase de Expando:

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

De esta forma, employee tiene un atributo name con el valor 'Sandy', un atributo age con el valor None y un atributo dinámico location con el valor 'SF'.

Para crear una subclase Expando cuyas propiedades no estén indexadas, define _default_indexed = False en la definición de la subclase:

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)
# }

También puedes definir _default_indexed en una entidad Expando. En ese caso, afectará a todas las propiedades asignadas después de que se haya definido.

Otra técnica útil es consultar un tipo Expando para una propiedad dinámica. Una consulta como la siguiente

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

no funcionará porque la clase no tiene un objeto de propiedad para la propiedad de ubicación. En su lugar, usa GenericProperty, la clase que usa Expando para las propiedades dinámicas:

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

Usar ganchos de modelo

NDB ofrece un mecanismo de hooking ligero. Al definir un enlace, una aplicación puede ejecutar código antes o después de un tipo de operación. Por ejemplo, un Model puede ejecutar una función antes de cada get().

Una función de gancho se ejecuta cuando se usan las versiones síncronas, asíncronas y múltiples del método correspondiente. Por ejemplo, un hook "pre-get" se aplicaría a todos los elementos de get(), get_async() y get_multi(). Hay versiones anteriores y posteriores a la RPC de cada hook.

Los hooks pueden ser útiles para lo siguiente:

  • Almacenamiento en caché de consultas
  • Auditoría de la actividad de Cloud Datastore por usuario
  • Imitar activadores de bases de datos

En el siguiente ejemplo se muestra cómo definir funciones 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

Si usas post-hooks con APIs asíncronas, los hooks se activan llamando a check_result(), get_result() o cediendo (dentro de un tasklet) el futuro de un método asíncrono. Los post-hooks no comprueban si la llamada a procedimiento remoto se ha realizado correctamente, sino que se ejecutan independientemente de si se ha producido un error.

Todos los post-hooks tienen un argumento Future al final de la firma de la llamada. Este objeto Future contiene el resultado de la acción. Puedes llamar a get_result() en este Future para obtener el resultado. Puedes estar seguro de que get_result() no se bloqueará, ya que Future se habrá completado cuando se llame al hook.

Si se genera una excepción durante un pre-gancho, la solicitud no se llevará a cabo. Aunque los ganchos se activan dentro de los métodos <var>*</var>_async, no puedes anticiparte a una llamada a procedimiento remoto generando tasklets.Return en un gancho previo a la llamada a procedimiento remoto.

Siguientes pasos