Referencia de propiedad de la entidad

Firestore en modo Datastore (Datastore) admite una variedad de tipos de datos para valores de propiedad. Se incluyen, entre otros:

  • Números enteros
  • Números de coma flotante
  • Strings
  • Fechas
  • Datos binarios

Para obtener una lista completa de los tipos, consulta Tipos de valores y propiedades.

Tipos de valores y propiedades

Los valores de datos asociados con una entidad constan de una o más propiedades. Cada propiedad tiene un nombre y uno o más valores. Una propiedad puede tener valores de más de un tipo, y dos entidades pueden tener valores de diferentes tipos para la misma propiedad. Las propiedades pueden o no estar indexadas (las consultas que ordenan o filtran en una propiedad P ignorarán las entidades en las que P no esté indexada). Una entidad puede tener como máximo 20,000 propiedades indexadas.

Se admiten los tipos de valor siguientes:

Cuando una consulta incluye una propiedad con valores de varios tipos, Datastore usa un orden determinista basado en las representaciones internas:

  1. Valores nulos
  2. Números de coma fija
    • Números enteros
    • fechas y horarios
  3. Valores booleanos
  4. Secuencias de bytes
    • String de Unicode
    • Claves de Blobstore
  5. números de coma flotante
  6. Claves de Datastore

Las strings de texto largas y las strings de bytes largas no están indexadas y no tienen un orden definido.

Tipos de propiedad

NDB es compatible con las funciones siguientes:

Tipo de propiedad Descripción
IntegerProperty Número entero de 64 bits con firma.
FloatProperty Número de punto flotante de doble precisión
BooleanProperty Booleano
StringProperty String de Unicode; hasta 1,500 bytes, indexada
TextProperty Strings de Unicode; longitud ilimitada, no indexada
BlobProperty String de bytes sin interpretar:
si estableces indexed=True, hasta 1500 bytes, indexado;
i indexed es False (el valor predeterminado), longitud ilimitada sin indexar.
Argumento de palabra clave opcional: compressed.
DateTimeProperty Fecha y hora (consulta Propiedades de fecha y hora)
DateProperty Fecha (consulta Propiedades de fecha y hora)
TimeProperty Hora (consulta Propiedades de fecha y hora)
GeoPtProperty Ubicación geográfica. Esta es un objeto ndb.GeoPt. El objeto tiene atributos lat y lon, ambos son números de punto flotante. Puedes construir uno con dos números de punto flotante como ndb.GeoPt(52.37, 4.88) o con una string ndb.GeoPt("52.37, 4.88"). Esta es en realidad la misma clase que db.GeoPt.
KeyProperty Clave de Datastore
Argumento de palabra clave opcional: tipo=kind, para requerir que las claves asignadas a esta propiedad siempre tengan el tipo indicado. Puede ser una string o una subclase Model.
BlobKeyProperty Clave de Blobstore
Corresponde a BlobReferenceProperty en la API de base de datos anterior, pero el valor de la propiedad es BlobKey en lugar de BlobInfo; puedes construir un BlobInfo con estos a través de BlobInfo(blobkey).
UserProperty Objeto de usuario.
StructuredProperty Incluye un tipo de modelo dentro de otro, según el valor (consulta Propiedades estructuradas)
LocalStructuredProperty Al igual que StructuredProperty, pero la representación en el disco es un BLOB opaco y no está indexado (consulta Propiedades estructuradas).
Argumento de palabra clave opcional: compressed.
JsonProperty El valor es un objeto de Python (como una lista, un diccionario o una string) que se puede serializar con el módulo de Python json. Datastore almacena la serialización de JSON como un blob. Sin indexar de forma predeterminada.
Argumento de palabra clave opcional: compressed.
PickleProperty El valor es un objeto de Python (como una lista, un diccionario o una string) que se puede serializar con el protocolo de serialización de Python. Datastore almacena la serialización como un blob. Sin indexar de forma predeterminada.
Argumento de palabra clave opcional: compressed.
GenericProperty Valor genérico
En su mayor parte, lo usa la clase Expando, pero también se puede usar de manera explícita. Su tipo puede ser cualquiera de estos: int, long, float, bool, str, unicode, datetime, Key, BlobKey, GeoPt, User o None.
ComputedProperty Valor computado de otras propiedades a partir de una función definida por el usuario. (Consulta Propiedades computadas).

Algunas de estas propiedades tienen un argumento de palabra clave opcional, compressed. Si la propiedad tiene compressed=True, entonces sus datos se comprimen con gzip en el disco. Ocupa menos espacio, pero necesita CPU para codificarse o decodificarse en las operaciones de escritura y lectura.

Tanto la compresión como la descompresión son "lentas"; un valor de propiedad comprimido solo se descomprimirá la primera vez que accedas a él. Si lees una entidad que contiene un valor de propiedad comprimida y lo escribes sin acceder a esa propiedad, no se descomprimirá ni comprimirá en absoluto. La memoria almacenada en caché en contexto también participa en este esquema lento, pero Memcache siempre almacena el valor comprimido para propiedades contraídas.

Debido al tiempo adicional que la CPU necesita para la compresión, generalmente es mejor usar propiedades comprimidas solo si los datos fueran demasiado grandes para ajustarse sin ellos. Recuerda que la compresión basada en gzip generalmente no es efectiva para imágenes y otros datos de medios, porque esos formatos ya están comprimidos con un algoritmo de compresión específico de medios (p. ej., JPEG para imágenes).

Opciones de propiedad

La mayoría de los tipos de propiedades admiten algunos argumentos estándares. El primero es un argumento de posicionamiento opcional que especifica el nombre de la propiedad de Datastore. Puedes utilizar esta opción para darle a la propiedad un nombre diferente en Datastore que en el punto de vista de la aplicación. Un uso común es reducir el espacio en Datastore para que use nombres de propiedad abreviados, mientras que tu código usa nombres más largos y significativos. Por ejemplo:

class Employee(ndb.Model):
    full_name = ndb.StringProperty('n')
    retirement_age = ndb.IntegerProperty('r')

Esto es muy útil para propiedades repetidas en las que esperas muchos valores por entidad.

Además, la mayoría de los tipos de propiedad admiten los argumentos de palabras clave siguientes:

Argumento Tipo Predeterminado Descripción
indexed bool Generalmente True.Incluye la propiedad en los índices de Datastore; si es False, los valores no se pueden consultar, pero las escrituras son más rápidas. No todos los tipos de propiedad admiten la indexación; configurar indexedcomo True falla en estos casos.
Las propiedades no indexadas cuestan menos operaciones de escritura que las propiedades indexadas.
repeated bool False El valor de propiedad es una lista de Python que contiene valores del tipo subyacente (consulta Propiedades repetidas).
No se puede combinar con required=True ni default=True.
required bool False La propiedad debe tener un valor especificado.
default Tipo subyacente de la propiedad Ninguno Valor predeterminado de la propiedad si no se especifica explícitamente.
choices Lista de valores de tipo subyacente None Lista opcional de valores permitidos.
validator Función None

Función opcional para validar y posiblemente forzar el valor.

Se llamará con argumentos (prop, value) y debe mostrar el valor (posiblemente forzado) o generar una excepción. Volver a llamar a la función en un valor forzado no debería modificar más el valor. (Por ejemplo, está bien que se muestre value.strip() o value.lower(), pero no value + '$'). También se puede mostrar None, que significa "no hay cambios". Consulta también Escribir subclases de propiedades.

verbose_name string None

Etiqueta HTML opcional para usar en formularios web de marcos de trabajo, como jinja2.

Propiedades repetidas

Cualquier propiedad con repeated=True se convierte en una propiedad repetida. La propiedad toma una lista de valores del tipo subyacente, en lugar de un solo valor. Por ejemplo, el valor de una propiedad definida con IntegerProperty(repeated=True) es una lista de número enteros.

Datastore puede ver varios valores de esa propiedad. Se crea un registro de índice separado para cada valor. Esto afecta la semántica de consulta; consulta Consultar propiedades repetidas para ver un ejemplo.

Este ejemplo usa una propiedad repetida:

class Article(ndb.Model):
    title = ndb.StringProperty()
    stars = ndb.IntegerProperty()
    tags = ndb.StringProperty(repeated=True)
...
article = Article(
    title='Python versus Ruby',
    stars=3,
    tags=['python', 'ruby'])
article.put()

Esto crea una entidad de Datastore con el siguiente contenido:

assert article.title == 'Python versus Ruby'
assert article.stars == 3
assert sorted(article.tags) == sorted(['python', 'ruby'])

Cuando se consulta la propiedad tags, esta entidad satisfará una consulta para 'python' o 'ruby'.

Cuando se actualiza una propiedad repetida, puedes asignarle una lista nueva o mutar la lista existente allí. Cuando asignas una lista nueva, los tipos de los elementos de lista se validan inmediatamente. Los tipos de elementos no válidos (por ejemplo, la asignación de [1, 2] a art.tags anterior) muestran una excepción. Cuando mutas la lista, el cambio no se valida inmediatamente. En su lugar, el valor se validará cuando escribas la entidad en Datastore.

Datastore conserva el orden de los elementos de lista en una propiedad repetida, por lo que puede asignar algún significado a su orden.

Propiedades de fecha y hora

Hay tres tipos de propiedades disponibles para almacenar valores relacionados con la fecha y la hora:

  • DateProperty
  • TimeProperty
  • DateTimeProperty

Estos toman valores pertenecientes a las clases correspondientes (date, time, datetime) del módulo estándar de Python datetime. El más general de los tres es DateTimeProperty, que denota tanto una fecha del calendario como una hora del día; los otros son ocasionalmente útiles para fines especiales que son obligatorios en una fecha (como una fecha de nacimiento) o en una hora (como una hora de reunión). Por razones técnicas, DateProperty y TimeProperty son subclases de DateTimeProperty , pero no debes depender de esta relación de herencia (y ten en cuenta que difiere de las relaciones de herencia entre las clases subyacentes definidas por el mismo módulo datetime).

Nota: Los tiempos de reloj de App Engine siempre se expresan en tiempo universal coordinado (UTC). Esto es relevante si usas la fecha o la hora actuales (datetime.datetime.now()) como un valor o una conversión entre objetos datetime y marcas de tiempo POSIX o tuplas de tiempo. Sin embargo, no se almacena información explícita de la zona horaria en Datastore, por lo que, si tienes cuidado, puedes usarlos para representar las horas locales en cualquier zona horaria, si usas la hora actual o las conversiones.

Cada una de estas propiedades tiene dos opciones de palabras clave booleanas adicionales:

Opción Descripción
auto_now_add Establece la propiedad en la fecha/hora actual cuando se crea la entidad. Puedes anular manualmente esta propiedad. Cuando la entidad se actualiza, la propiedad no cambia. Para ese comportamiento, usa auto_now.
auto_now Establece la propiedad en la fecha/hora actual cuando se crea la entidad y cada vez que se actualiza.

Estas opciones no se pueden combinar con repeated=True. Ambos se configuran como el valor predeterminado False. Si ambos están configurados como True, auto_now tiene prioridad. Es posible anular el valor de una propiedad con auto_now_add=True, pero no de una con auto_now=True. El valor automático no se genera hasta que se escribe la entidad; es decir, estas opciones no proporcionan valores predeterminados dinámicos. (Estos detalles difieren de la API de db anterior).

Nota: Cuando una transacción que escribe una propiedad con auto_now_add=True falla y se vuelve a intentar, volverá a usar el mismo valor de tiempo que en el intento original, en lugar de actualizarlo hasta el momento del reintento. Si la transacción falla de forma permanente, el valor de la propiedad se seguirá estableciendo en la copia en memoria de la entidad.

Propiedades estructuradas

Puedes estructurar las propiedades de un modelo. Por ejemplo, puedes definir una clase de modelo Contact que contenga una lista de direcciones, cada una con estructura interna. Las propiedades estructuradas (de tipo StructuredProperty) te permiten hacer eso, como se muestra a continuación:

class Address(ndb.Model):
    type = ndb.StringProperty()  # E.g., 'home', 'work'
    street = ndb.StringProperty()
    city = ndb.StringProperty()
...
class Contact(ndb.Model):
    name = ndb.StringProperty()
    addresses = ndb.StructuredProperty(Address, repeated=True)
...
guido = Contact(
    name='Guido',
    addresses=[
        Address(
            type='home',
            city='Amsterdam'),
        Address(
            type='work',
            street='Spear St',
            city='SF')])

guido.put()

Esto crea una entidad única de Datastore con las siguientes propiedades:

assert guido.name == 'Guido'
addresses = guido.addresses
assert addresses[0].type == 'home'
assert addresses[1].type == 'work'
assert addresses[0].street is None
assert addresses[1].street == 'Spear St'
assert addresses[0].city == 'Amsterdam'
assert addresses[1].city == 'SF'

Si se vuelve a leer, esa entidad reconstruye exactamente la entidad de Contact original. Aunque las instancias de Address se definen con la misma sintaxis que las clases de modelo, no son entidades completas. No tienen sus propias claves en Datastore. No se pueden recuperar independientemente de la entidad de Contact a la que pertenecen. Sin embargo, una aplicación puede consultar los valores de sus campos individuales; consulta Filtra por valores de propiedad estructurada. Ten en cuenta que address.type, address.street y address.city se leen como arreglos paralelos desde el punto de vista de Datastore, pero la biblioteca de NDB oculta este aspecto y construye la lista correspondiente de instancias de Address.

Puedes especificar las opciones de propiedad habituales para las propiedades estructuradas (excepto indexed). El nombre de Datastore es el segundo argumento de posicionamiento en este caso (el primero es la clase de modelo que se usa para definir la subestructura).

Cuando no necesitas consultar las propiedades internas de una subestructura, puedes usar una propiedad local estructurada (LocalStructuredProperty) en su lugar. Si reemplazas StructuredProperty por LocalStructuredProperty en el ejemplo anterior, el comportamiento del código de Python es el mismo, pero Datastore ve solo un BLOB opaco para cada dirección. La entidad guido que se crea en el ejemplo, se almacenaría de esta forma: name = "Guido" address = <opaque blob for {"type": "home", "city": "Amsterdam"}> address = <opaque blob for {"type": "work", "city": "SF", "street": "Spear St"}>

La entidad se leerá con éxito. Como las propiedades de este tipo nunca están indexadas, no puedes consultar los valores de dirección.

Nota: StructuredProperty con una propiedad anidada (esté o no estructurada) admite solo una capa única de propiedades repetidas. Se puede repetir StructuredProperty o la propiedad anidada, pero no ambas. Una solución alternativa es usar LocalStructuredProperty, que no tiene esta restricción (pero no permite realizar consultas sobre los valores de sus propiedades).

Propiedades computadas

Las propiedades calculadas (ComputedProperty) son propiedades de solo lectura cuyo valor se calcula a partir de otros valores de propiedad mediante una función suministrada por la aplicación. Ten en cuenta que una propiedad calculada solo admite los tipos que son compatibles con propiedades genéricas. El valor calculado se escribe en Datastore para que se pueda consultar y mostrar en el lector de Datastore, pero el valor almacenado se ignora cuando la entidad vuelve a leer desde Datastore; por el contrario, el valor se vuelve a calcular llamando a la función cada vez que se solicita el valor. Por ejemplo:

class SomeEntity(ndb.Model):
    name = ndb.StringProperty()
    name_lower = ndb.ComputedProperty(lambda self: self.name.lower())
...
entity = SomeEntity(name='Nick')
entity.put()

Esto almacena una entidad con los valores de propiedad siguientes:

assert entity.name == 'Nick'
assert entity.name_lower == 'nick'

Si cambiamos el nombre a "Nickie" y pedimos el valor de name_lower, muestra "nickie":

entity.name = 'Nick'
assert entity.name_lower == 'nick'
entity.name = 'Nickie'
assert entity.name_lower == 'nickie'

Nota: Usa ComputedProperty si la aplicación realiza una consulta para el valor calculado. Si solo deseas usar la versión derivada en el código de Python, define un método normal o usa la función incorporada @property de Python.

Nota: Si creas un modelo sin una clave especificada de forma manual y, en su lugar, confías en Datastore para generar automáticamente el ID de la entidad, entonces, en tu primer put(), ComputedProperty no podrá leer el campo del ID, ya que el campo se calcula antes de que se genere el ID. Si necesitas una ComputedProperty que use el ID de la entidad, puedes usar el método allocate_ids para generar un ID y una clave con los cuales crear la entidad, de modo que tu ComputedProperty pueda hacer referencia a ese ID en el primer put() de la entidad.

Propiedades del mensaje del protocolo de RPC de Google

La biblioteca del RPC del protocolo de Google usa objetos Message para datos estructurados. Pueden representar solicitudes RPC, respuestas y otros elementos. NDB proporciona una API para almacenar objetos Message del RPC del protocolo de Google como propiedades de la entidad. Supongamos que defines una subclase Message:

from protorpc import messages
...
class Note(messages.Message):
    text = messages.StringField(1, required=True)
    when = messages.IntegerField(2)

Puedes almacenar objetos Note en Datastore como valores de propiedad de la entidad mediante la API msgprop de NDB.

from google.appengine.ext import ndb
from google.appengine.ext.ndb import msgprop
...
class NoteStore(ndb.Model):
    note = msgprop.MessageProperty(Note, indexed_fields=['when'])
    name = ndb.StringProperty()
...
my_note = Note(text='Excellent note', when=50)

ns = NoteStore(note=my_note, name='excellent')
key = ns.put()

new_notes = NoteStore.query(NoteStore.note.when >= 10).fetch()

Si deseas consultar los nombres de los campos, estos deben estar indexados. Puedes especificar una lista de nombres de campo que se indexarán con el parámetro indexed_fields para MessageProperty.

MessageProperty admite muchas de las opciones de propiedad, pero no todas. Es compatible con:

  • name
  • repeated
  • required
  • default
  • choices
  • validator
  • verbose_name

Las propiedades de mensaje no admiten la opción de propiedad indexed. No puedes indexar los valores Message. (Puedes indexar los campos de un mensaje como se describe anteriormente).

Los mensajes anidados (que usan MessageField) también funcionan:

class Notebook(messages.Message):
    notes = messages.MessageField(Note, 1, repeated=True)
...
class SignedStorableNotebook(ndb.Model):
    author = ndb.StringProperty()
    nb = msgprop.MessageProperty(
        Notebook, indexed_fields=['notes.text', 'notes.when'])

MessageProperty tiene una opción de propiedad especial, protocol, que especifica cómo se serializa el objeto de mensaje en Datastore. Los valores son nombres de protocolo como los usa la clase protorpc.remote.Protocols. Los nombres de protocolo admitidos son protobuf y protojson. El valor predeterminado es protobuf.

msgprop también define EnumProperty, un tipo de propiedad que se puede usar para almacenar un valor protorpc.messages.Enum en una entidad. Ejemplo:

class Color(messages.Enum):
    RED = 620
    GREEN = 495
    BLUE = 450
...
class Part(ndb.Model):
    name = ndb.StringProperty()
    color = msgprop.EnumProperty(Color, required=True)
...
p1 = Part(name='foo', color=Color.RED)
print p1.color  # prints "RED"

EnumProperty almacena el valor como un número entero. De hecho, EnumProperty es una subclase de IntegerProperty. Esto implica que puedes cambiar el nombre de sus valores de enumeración sin tener que modificar las entidades ya almacenadas, pero no puedes renumerarlos.

EnumProperty admite las opciones de propiedad siguientes:

  • name
  • indexed
  • repeated
  • required
  • default
  • choices
  • validator
  • verbose_name

Acerca de los modelos de entidad NDB

Un modelo de entidad NDB puede definir propiedades. Las propiedades de la entidad son un poco como miembros de datos de las clases de Python, una forma estructurada de almacenar datos; también son como campos en un esquema de base de datos.

Una aplicación típica define un modelo de datos definiendo una clase que hereda de Model con algunos atributos de clase de propiedad. Por ejemplo:


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

Aquí, username, userid y email son propiedades de Account.

Hay muchos otros tipos de propiedades. Algunos son útiles para representar fechas y horas, y tienen funciones de actualización automática convenientes.

Una aplicación puede ajustar el comportamiento de una propiedad especificando opciones en la propiedad. Estos pueden facilitar la validación, establecer valores predeterminados o cambiar la indexación de consultas.

Un modelo puede tener propiedades más complejas. Las propiedades repetidas son como listas. Las propiedades estructuradas son como objetos. Las propiedades computadas de solo lectura se definen a través de funciones; esto facilita la definición de una propiedad en términos de una o más propiedades. Los modelos de Expando pueden definir propiedades de manera dinámica.