Crea modelos de entidades

Necesitas crear una clase modelo para tu entidad. Puedes hacerlo de dos maneras:

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

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

Cómo crear una clase de modelo con propiedades

Antes de crear una entidad, debes crear una clase de modelo que defina una o más 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()

En este ejemplo la intención es que cada entidad Account tenga propiedades de nombre de usuario, ID de usuario y correo electrónico.

Para ver la lista completa de los tipos de propiedad, consulta Referencia de propiedades de las entidades.

Cómo crear un clase de modelo Expando

No es necesario que uses una clase de modelo que defina las propiedades por adelantado. Existe una subclase de modelo especial denominada Expando que cambia el comportamiento de sus entidades para que se guarden en el almacén de datos todos los atributos que se les asignen. Ten en cuenta que estos atributos no pueden empezar con un guion bajo.

A continuación, se presenta 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()

Esto escribe una entidad en el almacén de datos con una propiedad foo con un número entero 1, una propiedad bar con valor de string 'blah' y una propiedad tag repetida con los valores de string 'exp', 'and' y 'oh'. Las propiedades están indexadas y puedes inspeccionarlas mediante el atributo _properties de la entidad:

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

Un Expando creado cuando se obtiene un valor del almacén de datos tiene propiedades para todos los valores de propiedad que se guardaron en el almacén de datos.

Una aplicación puede agregar propiedades predefinidas a una subclase Expando:

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

Esto le da a employee un atributo name con valor 'Sandy', un atributo age con valor None y un atributo dinámico location con valor 'SF'.

Para crear una subclase Expando cuyas propiedades no estén indexadas, establece _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 establecer _default_indexed en una entidad Expando. En este caso, afectará a todas las propiedades asignadas después de configurarlo.

Otra técnica útil es consultar un tipo Expando de una propiedad dinámica. Considere la consulta que se muestra a continuación:

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

Esta consulta no funcionará porque la clase no tiene un objeto de propiedad llamado location. En su lugar, usa GenericProperty, la clase que Expando usa para las propiedades dinámicas:

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

Cómo usar enlaces de modelo

NDB ofrece un mecanismo de enlaces sencillo. Cuando se define un hook, una aplicación puede ejecutar algún código antes o después de algún tipo de operaciones; por ejemplo, un Model podría ejecutar alguna función antes de cada get().

Una función de enlace se ejecuta cuando se usan las versiones síncrona, asíncrona y múltiple del método adecuado. Por ejemplo, un hook “pre-get” se aplicaría a todo get(), get_async() y get_multi(). Hay versiones de cada enlace previas y posteriores a RPC.

Los enlaces pueden ser útiles para lo siguiente:

  • almacenar consultas en caché
  • hacer una auditoría de la actividad de Cloud Datastore por usuario
  • imitar activadores de la base de datos

El ejemplo siguiente muestra cómo definir funciones de enlace:

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 hooks posteriores con API asíncronas, los hooks se activan cuando se llama a check_result() o get_result(), o mediante la producción (dentro de un tasklet) del futuro de un método asíncrono. Los enlaces posteriores no verifican si el RPC se ejecutó correctamente; el enlace se ejecuta sin importar la falla.

Todos los enlaces posteriores tienen un argumento Future al final de la firma de la llamada. Este objeto Future conserva el resultado de la acción. Puedes llamar a get_result() en este Future para recuperar el resultado. También puedes estar seguro de que get_result() no causará bloqueos, ya que Future se completa cuando se llama al hook.

Si se genera una excepción durante un enlace previo, se evita que se realice la solicitud. Aunque los hooks se activan dentro de los métodos <var>*</var>_async, no puedes adelantar una RPC mediante la generación de tasklets.Return en un hook previo a RPC.

¿Qué sigue?