Nota: Se recomienda enfáticamente a los desarrolladores que compilan aplicaciones nuevas que usen la biblioteca cliente de NDB, ya que tiene muchas ventajas en comparación con esta biblioteca cliente, como el almacenamiento en caché automático de entidades mediante la API de Memcache. Si por el momento usas la biblioteca cliente de DB anterior, lee la Guía de migración de DB a NDB.
Datastore proporciona acceso programático a algunos de los metadatos a fin de admitir la metaprogramación, implementar funciones administrativas de backend, simplificar el almacenamiento en caché coherente y propósitos similares. Puedes usarlo, por ejemplo, si deseas compilar un visualizador de Datastore personalizado para la aplicación. Los metadatos disponibles incluyen información sobre los grupos de entidades, los espacios de nombres, los tipos de entidad y las propiedades que usa tu aplicación, así como las representaciones de propiedad para cada propiedad.
El panel de Datastore en la consola de Google Cloud también proporciona algunos metadatos sobre la aplicación, pero los datos que se muestran allí difieren de los que muestran estas funciones en algunos aspectos importantes.
- Actualidad: Cuando se leen los metadatos con la API, se obtienen los datos actuales, mientras que los datos del panel se actualizan solo una vez por día.
- Contenido: Algunos metadatos del panel no están disponibles mediante las API y viceversa.
- Velocidad: Las consultas y las solicitudes GET de metadatos se facturan de la misma forma que las de Datastore. En general, las consultas de metadatos que recuperan información sobre espacios de nombres, tipos y propiedades son de ejecución lenta. Como regla general, puedes esperar que una consulta de metadatos que muestra N entidades demore el mismo tiempo que N consultas ordinarias que muestran una sola entidad cada una. Además, las consultas de representación de propiedad (consultas de propiedad que no son de solo claves) son más lentas que las consultas de propiedad de solo claves. Las operaciones get de metadatos de grupos de entidades son algo más rápidas que las de una entidad normal.
Funciones auxiliares
Las siguientes funciones obtienen información de metadatos:
get_entity_group_version()
obtiene un número de versión para un grupo de entidades. Esto es útil a fin de averiguar si alguna entidad del grupo cambió desde la última vez que obtuviste el número de versión.get_namespaces()
muestra una lista que contiene los nombres de todos los espacios de nombres de una aplicación o aquellos en un rango especificado.get_kinds()
muestra una lista que contiene los nombres de todas las categorías de entidades de una aplicación o aquellos en un rango especificado.get_properties_of_kind()
muestra una lista que contiene los nombres de todas las propiedades indexadas de una aplicación (o las de un rango especificado) asociadas con una categoría de entidad determinada. No se incluyen las propiedades que no están indexadas.get_representations_of_kind()
muestra un diccionario que contiene las representaciones de todas las propiedades indexadas de una aplicación o que se encuentran en un rango especificado asociado con una categoría de entidad determinada. El diccionario asigna el nombre de cada propiedad a una lista de las representaciones de esa propiedad. No se incluyen las propiedades que no están indexadas.
Metadatos de grupo de entidades
Cloud Datastore brinda acceso a la “versión” de un grupo de entidades, un número estrictamente positivo que aumenta con cada cambio que se realice en el grupo de entidades.
El siguiente ejemplo muestra cómo obtener la versión de un grupo de entidades:
from google.appengine.ext import db
from google.appengine.ext.db import metadata
class Simple(db.Model):
x = db.IntegerProperty()
entity1 = Simple(x=11)
entity1.put()
# Print entity1's entity group version
print 'version', metadata.get_entity_group_version(entity1)
# Write to a different entity group
entity2 = Simple(x=22)
entity2.put()
# Will print the same version, as entity1's entity group has not changed
print 'version', metadata.get_entity_group_version(entity1)
# Change entity1's entity group by adding a new child entity
entity3 = Simple(x=33, parent=entity1.key())
entity3.put()
# Will print a higher version, as entity1's entity group has changed
print metadata.get_entity_group_version(entity1)
Comportamiento heredado
Según el comportamiento heredado de la versión del grupo de entidades, esa versión aumenta solo cuando se incorporan cambios en el grupo de entidades. El comportamiento heredado de los metadatos del grupo de entidades se podría usar, por ejemplo, para mantener una caché coherente de una consulta principal compleja en un grupo de entidades.
En este ejemplo, se almacenan en caché los resultados de consultas (un recuento de resultados coincidentes) y se usa el comportamiento heredado de las versiones de grupos de entidades para emplear el valor almacenado en caché si es actual:
from google.appengine.api import memcache
from google.appengine.ext import db
from google.appengine.ext.db import metadata
def count_entity_group(entity_group_key):
"""Count the entities in the specified entity group."""
# Check if we have a cached version of the current entity group count
cached = memcache.get(str(entity_group_key))
if cached:
(version, count) = cached
# Is the cached value for the current version?
if version == metadata.get_entity_group_version(entity_group_key):
return count
def tx():
# Need to actually count entities. Using a transaction to get a consistent
# count and entity group version.
count = db.Query(keys_only=True).ancestor(entity_group_key).count(limit=5000)
# Cache the count and the entity group version
version = metadata.get_entity_group_version(entity_group_key)
memcache.set(str(entity_group_key), (version, count))
return count
return db.run_in_transaction(tx)
get_entity_group_version()
puede mostrar el valor None
para un grupo de entidades en el que nunca realizaron operaciones de escritura.
Las versiones del grupo de entidades se obtienen mediante una llamada a get()
en una seudoentidad especial que contiene una propiedad __version__
. Consulta la documentación de referencia sobre EntityGroup para obtener más detalles.
Consultas de metadatos
Si las funciones auxiliares descritas en la sección anterior no satisfacen tus necesidades, puedes emitir solicitudes de metadatos más flexibles o elaboradas con una consulta de metadatos explícita. En Python, las clases de modelo para ese tipo de consultas se definen en el paquete google.appengine.ext.db.metadata
. Estos modelos proporcionan categorías de entidad especiales que se reservan para consultas de metadatos:
Clase del modelo | Tipo de entidad |
---|---|
Namespace |
__namespace__ |
Kind |
__kind__ |
Property |
__property__ |
Estos modelos y tipos no entrarán en conflicto con otros del mismo nombre que puedan existir en tu aplicación. Si consultas estas categorías especiales, puedes recuperar entidades que contengan los metadatos deseados.
Las entidades que muestran las consultas de metadatos se generan de forma dinámica, según el estado actual de Datastore. Si bien puedes crear instancias locales de las clases de modelo Namespace
, Kind
o Property
, cualquier intento de almacenarlas en Datastore fallará con una excepción BadRequestError
.
Puedes emitir consultas de metadatos mediante un objeto de consulta que pertenezca a una de estas dos clases:
- Un objeto
Query
que muestra el método de claseNamespace.all()
,Kind.all()
oProperty.all()
(heredado del método de superclaseModel.all()
) - Un objeto
GqlQuery
para consultas de estilo GQL
En el siguiente ejemplo, se muestran los nombres de todas las categorías de entidades de una aplicación:
from google.appengine.ext import db
from google.appengine.ext.db.metadata import Kind
for k in Kind.all():
print "kind: '%s'" % k.kind_name
Consultas de espacio de nombres
Si la aplicación usa la API de espacios de nombres, puedes usar una consulta de espacio de nombres para encontrar todos los que se usan en las entidades de la aplicación. Esto te permite realizar actividades, como funciones administrativas, en varios espacios de nombres.
Las consultas de espacio de nombres muestran entidades de la categoría especial __namespace__
cuyo nombre de clave es el nombre de un espacio de nombres. Una excepción a esto es el espacio de nombres predeterminado que designa la string vacía ""
; debido a que la string vacía no es un nombre de clave válido, este espacio de nombres recibe una clave con el ID numérico 1
en su lugar. Las consultas de este tipo solo admiten filtros para rangos superiores a la seudopropiedad especial __key__
, cuyo valor es la clave de la entidad. Los resultados se pueden ordenar por valor ascendente (pero no descendente) de __key__
. Debido a que las entidades __namespace__
no tienen propiedades, las consultas de solo claves y las que no son de solo claves muestran la misma información.
Las entidades del espacio de nombres son instancias de la clase de modelo google.appengine.ext.db.metadata.Namespace
. La propiedad de string namespace_name
, calculada a partir de la clave de la entidad, muestra el nombre del espacio de nombres correspondiente
(si la clave tiene un ID numérico 1
, la propiedad muestra la string vacía). Para facilitar la consulta, el modelo Namespace
proporciona los siguientes métodos de clase:
Namespace.key_for_namespace()
compila una clave__namespace__
a partir de un nombre de espacio de nombres.Namespace.key_to_namespace()
muestra el nombre del espacio de nombres correspondiente a una clave__namespace__
determinada.
A modo de ejemplo, a continuación, se muestra la implementación de la función auxiliar get_namespaces()
, que muestra una lista que contiene los nombres de todos los espacios de nombres de una aplicación (o aquellos en el rango entre dos nombres especificados, start
y end
):
from google.appengine.ext import db
from google.appengine.ext.db.metadata import Namespace
def get_namespaces(start=None, end=None):
# Start with unrestricted namespace query
q = Namespace.all()
# Limit to specified range, if any
if start is not None:
q.filter('__key__ >=', Namespace.key_for_namespace(start))
if end is not None:
q.filter('__key__ <', Namespace.key_for_namespace(end))
# Return list of query results
return [ns.namespace_name for ns in q]
Consultas de tipos
Las consultas de categorías muestran entidades de categoría __kind__
cuyo nombre de clave es el nombre de una categoría de entidad. Las consultas de este tipo están restringidas de forma implícita al espacio de nombres actual y admiten el filtrado solo para rangos sobre la seudopropiedad __key__
. Los resultados se pueden ordenar por valor ascendente (pero no descendente) de __key__
. Debido a que las entidades __kind__
no tienen propiedades, las consultas de solo claves y las que no son de solo claves muestran la misma información.
Las entidades de categoría son instancias de la clase de modelo google.appengine.ext.db.metadata.Kind
.
La propiedad de la string kind_name
, calculada a partir de la clave de la entidad, muestra el nombre de la categoría de entidad correspondiente. Para facilitar la consulta, el modelo Kind
proporciona los siguientes métodos de clase:
Kind.key_for_kind()
compila una clave__kind__
a partir de un nombre de categoría.Kind.key_to_kind()
muestra el nombre de categoría correspondiente a una clave__kind__
determinada.
A modo de ejemplo, a continuación, se muestra la implementación de la función auxiliar get_kinds()
, que muestra una lista que contiene los nombres de todas las categorías de entidad de una aplicación (o aquellos en el rango entre dos nombres especificados, start
y end
):
from google.appengine.ext import db
from google.appengine.ext.db.metadata import Kind
def get_kinds(start=None, end=None):
# Start with unrestricted kind query
q = Kind.all()
# Limit to specified range, if any
if start is not None and start != '':
q.filter('__key__ >=', Kind.key_for_kind(start))
if end is not None:
if end == '':
return [] # Empty string is not a valid kind name, so can't filter
q.filter('__key__ <', Kind.key_for_kind(end))
# Return list of query results
return [k.kind_name for k in q]
En el siguiente ejemplo, se muestran todas las categorías cuyos nombres comienzan con una letra minúscula:
from google.appengine.ext import db
from google.appengine.ext.db.metadata import Kind
# Start with unrestricted kind query
q = Kind.all()
# Limit to lowercase initial letters
q.filter('__key__ >=', Kind.key_for_kind('a'))
endChar = chr(ord('z') + 1) # Character after 'z'
q.filter('__key__ <', Kind.key_for_kind(endChar))
# Print query results
for k in q:
print k.kind_name
Consultas de propiedades
Las consultas de propiedades muestran entidades de categoría __property__
que denotan las propiedades asociadas con una categoría de entidad (sin importar si están definidas en la actualidad en el modelo de la categoría). La entidad que representa la propiedad P de la categoría K está compuesta de la siguiente manera:
- La clave de la entidad tiene el tipo
__property__
y el nombre de clave P. - La clave de la entidad principal tiene la categoría
__kind__
y el nombre de clave K.
Las entidades de propiedad son instancias de la clase de modelo google.appengine.ext.db.metadata.Property
. Las propiedades de string kind_name
y property_name
, calculadas a partir de la clave de la entidad, muestran los nombres de la categoría y la propiedad correspondientes. El modelo Property
proporciona cuatro métodos de clase para simplificar la compilación y el examen de las claves __property__
:
Property.key_for_kind()
compila una clave superior__kind__
para claves__property__
de una categoría de entidad especificada.Property.key_for_property()
compila una clave__property__
para una categoría y una propiedad especificadas.Property.key_to_kind()
muestra el nombre de categoría asociada con una clave__property__
.Property.key_to_property()
muestra el nombre de la propiedad asociada con una clave__property__
(oNone
si la clave especifica solo una categoría).
En el siguiente ejemplo, se muestran estos métodos:
from google.appengine.ext import db
from google.appengine.ext.db.metadata import Property
class Employee(db.Model):
name = db.StringProperty()
ssn = db.IntegerProperty()
employee_key = Property.key_for_kind("Employee")
employee_name_key = Property.key_for_property("Employee", "Name")
Property.key_to_kind(employee_key) # Returns "Employee"
Property.key_to_property(employee_name_key) # Returns "Name"
El comportamiento de una consulta de propiedad depende de si es una consulta de solo claves o no de solo claves (representación de propiedad), como se detalla en las subsecciones que siguen.
Consultas de propiedades: de solo claves
Las consultas de propiedad de solo claves muestran una clave para cada propiedad indexada de un tipo de entidad especificado. (No se incluyen las propiedades no indexadas). En el siguiente ejemplo, se muestran los nombres de todas las categorías de entidades de una aplicación y las propiedades asociadas a cada una:
from google.appengine.ext import db
from google.appengine.ext.db.metadata import Property
# Create unrestricted keys-only property query
q = Property.all(keys_only=True)
# Print query results
for p in q:
print "%s: %s" % (Property.key_to_kind(p), Property.key_to_property(p))
Las consultas de este tipo se restringen de forma implícita al espacio de nombres actual y solo admiten filtros para rangos superiores a la seudopropiedad __key__
, en los que las claves denotan entidades __kind__
o __property__
. Los resultados se pueden ordenar por valor ascendente (pero no descendente) de __key__
. Los filtros se aplican a los pares tipo-propiedad, ordenados primero por tipo y después por propiedad. Por ejemplo, imagina que tienes una entidad con las siguientes propiedades:
- tipo
Account
con propiedadesbalance
company
- tipo
Employee
con propiedadesname
ssn
- tipo
Invoice
con propiedadesdate
amount
- tipo
Manager
con propiedadesname
title
- tipo
Product
con propiedadesdescription
price
La consulta que muestra los datos de la propiedad se verá de la siguiente manera:
from google.appengine.ext import db
from google.appengine.ext.db.metadata import Property
# Start with unrestricted keys-only property query
q = Property.all(keys_only=True)
# Limit range
q.filter('__key__ >=', Property.key_for_property("Employee", "salary"))
q.filter('__key__ <=', Property.key_for_property("Manager", "salary"))
# Print query results
for p in q:
print "%s: %s" % (Property.key_to_kind(p), Property.key_to_property(p))
La consulta anterior mostraría lo siguiente:
Employee: ssn
Invoice: date
Invoice: amount
Manager: name
Observa que los resultados no incluyen la propiedad name
de tipo Employee
, la propiedad title
de tipo Manager
, ni ninguna propiedad de tipos Account
y Product
, porque quedan fuera del rango especificado para la consulta.
Las consultas de propiedades también admiten el filtrado principal en una clave __kind__
o __property__
para limitar los resultados de las consultas a un solo tipo o propiedad. Puedes usar esto, por ejemplo, para obtener las propiedades asociadas a una categoría de entidad determinada, como en el siguiente ejemplo:
get_properties_of_kind()
)
from google.appengine.ext import db
from google.appengine.ext.db.metadata import Property
def get_properties_of_kind(kind, start=None, end=None):
# Start with unrestricted keys-only property query
q = Property.all(keys_only=True)
# Limit to specified kind
q.ancestor(Property.key_for_kind(kind))
# Limit to specified range, if any
if start is not None and start != '':
q.filter('__key__ >=', Property.key_for_property(kind, start))
if end is not None:
if end == '':
return [] # Empty string is not a valid property name, so can't filter
q.filter('__key__ <', Property.key_for_property(kind, end))
# Return list of query results
return [Property.key_to_property(p) for p in q]
Consultas de propiedad: no de solo claves (representación de propiedad)
Las consultas de propiedades de no solo claves, conocidas como consultas de representación de propiedad, muestran información adicional sobre las representaciones que usa cada par tipo-propiedad. (no se incluyen las propiedades no indexadas). La entidad que se muestra para la propiedad P de tipo K tiene la misma clave que la de una consulta de solo claves correspondiente, junto con una propiedad property_representation
adicional que muestra las representaciones de la propiedad. El valor de esta propiedad es una instancia de la clase StringListProperty
que contiene una string para cada representación de propiedad P que se encuentra en cualquier entidad de la categoría K.
Ten en cuenta que las representaciones no son las mismas que las clases de propiedad; varias clases de propiedad se pueden asignar a la misma representación (por ejemplo, StringProperty
y PhoneNumberProperty
usan la representación STRING
).
La siguiente tabla muestra las asignaciones entre las clases de propiedad y sus representaciones:
Clase de propiedad | Representación |
---|---|
IntegerProperty |
INT64 |
FloatProperty |
DOUBLE |
BooleanProperty |
BOOLEAN |
StringProperty |
STRING |
ByteStringProperty |
STRING |
DateProperty |
INT64 |
TimeProperty |
INT64 |
DateTimeProperty |
INT64 |
GeoPtProperty |
POINT |
PostalAddressProperty |
STRING |
PhoneNumberProperty |
STRING |
EmailProperty |
STRING |
UserProperty |
USER |
IMProperty |
STRING |
LinkProperty |
STRING |
CategoryProperty |
STRING |
RatingProperty |
INT64 |
ReferenceProperty SelfReferenceProperty |
REFERENCE |
blobstore.BlobReferenceProperty |
STRING |
ListProperty |
Representación de elemento de lista |
StringListProperty |
Representación de elemento de lista |
A modo de ejemplo, a continuación, se muestra la implementación de la función auxiliar get_representations_of_kind()
, que muestra un diccionario que contiene las representaciones de todas las propiedades indexadas de una aplicación (o las que están en el rango entre dos nombres especificados, start
y end
) que se asocian con una categoría de entidad determinada. El diccionario asigna el nombre de cada propiedad a una lista de las representaciones de esa propiedad:
from google.appengine.ext import db
from google.appengine.ext.db.metadata import Property
def get_representations_of_kind(kind, start=None, end=None):
# Start with unrestricted non-keys-only property query
q = Property.all()
# Limit to specified kind
q.ancestor(Property.key_for_kind(kind))
# Limit to specified range, if any
if start is not None and start != '':
q.filter('__key__ >=', Property.key_for_property(kind, start))
if end is not None:
if end == '':
return [] # Empty string is not a valid property name, so can't filter
q.filter('__key__ <', Property.key_for_property(kind, end))
# Initialize result dictionary
result = {}
# Add query results to dictionary
for p in q:
result[p.property_name] = p.property_representation
# Return dictionary
return result