Observação: é altamente recomendável a desenvolvedores que criam novos aplicativos usar a biblioteca de cliente NDB, porque ela oferece diversos benefícios em comparação com esta biblioteca de cliente, como armazenamento em cache automático de entidades por meio da API Memcache. Se você estiver usando a antiga biblioteca de cliente DB, leia o Guia de migração de DB para NDB.
O Datastore fornece acesso programático a alguns de seus metadados para oferecer suporte à metaprogramação, implementar funções administrativas de back-end, simplificar o armazenamento em cache consistente e finalidades semelhantes. É possível usá-lo, por exemplo, para criar um visualizador personalizado do Datastore para seu aplicativo. Nos metadados disponíveis, há informações sobre grupos de entidades, namespaces, tipos de entidade e propriedades utilizadas pelo aplicativo, além de representações de cada propriedade.
O Painel do Datastore, no console do Google Cloud, também fornece alguns metadados sobre seu aplicativo, mas os dados exibidos nele diferem, em alguns aspectos importantes, dos retornados por essas funções.
- Atualização. A leitura de metadados pela API fornece dados atuais, enquanto os dados do painel são atualizados somente uma vez por dia.
- Conteúdo. Alguns metadados no painel não estão disponíveis através das APIs e vice-versa.
- Velocidade. Consultas e gets de metadados são faturados da mesma maneira que consultas e gets do Datastore. As consultas de metadados que buscam informações sobre namespaces, tipos e propriedades geralmente demoram a ser executadas. Como regra geral, uma consulta de metadados que retorna N entidades costuma levar mais ou menos o mesmo tempo que consultas N comuns, cada uma retornando uma única entidade. Além disso, as consultas de representação de propriedade, que são de propriedade sem chave, são mais lentas do que as consultas de propriedade apenas de chaves. Os resultados de metadados do grupo de entidades são um pouco mais rápidos do que uma entidade normal.
Funções auxiliares
As seguintes funções recebem informações de metadados:
get_entity_group_version()
recebe um número de versão para um grupo de entidades. Isso é útil para descobrir se alguma entidade no grupo foi alterada desde a última vez que você recebeu o número da versão.get_namespaces()
retorna uma lista contendo os nomes de todos os namespaces de um aplicativo ou aqueles em um intervalo especificado.get_kinds()
retorna uma lista contendo os nomes de todos os tipos de entidade de um aplicativo ou aqueles em um intervalo especificado.get_properties_of_kind()
retorna uma lista contendo os nomes de todas as propriedades indexadas de um aplicativo (ou aquelas em um intervalo especificado) associadas a um determinado tipo de entidade. Propriedades não indexadas não estão incluídas.get_representations_of_kind()
retorna um dicionário que contém as representações de todas as propriedades indexadas de um aplicativo ou aquelas em um intervalo especificado associado a um determinado tipo de entidade. O dicionário mapeia o nome de cada propriedade a uma lista das representações dessa propriedade. Propriedades não indexadas não estão incluídas.
Metadados do grupo de entidades
No Cloud Datastore é possível ter acesso à "versão" de um grupo de entidades, um número estritamente positivo que tem garantia de aumentar a cada alteração no grupo de entidades.
O seguinte exemplo mostra como receber a versão de um 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)
Comportamento legado
No comportamento legado da versão do grupo de entidades, a versão aumenta apenas em alterações no grupo de entidades. O comportamento legado dos metadados do grupo de entidades poderia ser usado, por exemplo, para manter um cache consistente de uma consulta de ancestral complexa em um grupo de entidades.
Neste exemplo, armazenamos em cache os resultados da consulta (uma contagem de resultados correspondentes) e usamos o comportamento legado das versões do grupo de entidades para utilizar o valor em cache se ele for atual:
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()
pode retornar None
para um grupo de entidades que
nunca foi gravado.
As versões de grupo de entidades são obtidas chamando get()
em uma pseudo-entidade especial
que contém uma propriedade __version__
. Consulte a documentação de referência em
EntityGroup para mais detalhes.
Consultas de metadados
Se as funções auxiliares descritas na seção anterior não atenderem às necessidades, você poderá emitir solicitações de metadados mais elaboradas ou flexíveis com uma consulta de metadados explícita. No Python, as classes de modelo para essas consultas são
definidas no
pacote
google.appengine.ext.db.metadata
. Esses modelos fornecem tipos de entidade especiais reservados para consultas de metadados:
Classe de modelo | Tipo de entidade |
---|---|
Namespace |
__namespace__ |
Kind |
__kind__ |
Property |
__property__ |
Esses modelos e tipos não entrarão em conflito com outros de nomes iguais que talvez já existam no aplicativo. Ao consultar esses tipos especiais, você pode recuperar entidades que contenham os metadados desejados.
As entidades retornadas por consultas de metadados são geradas dinamicamente, com base no
estado atual do Datastore. Embora seja possível criar instâncias
locais das classes de modelo
Namespace
, Kind
ou Property
, qualquer
tentativa de armazená-las no Datastore falhará
com uma
exceção
BadRequestError
.
É possível emitir consultas de metadados usando um objeto de consulta pertencente a uma de duas classes:
- Um objeto
Query
retornado pelo método de classeNamespace.all()
,Kind.all()
ouProperty.all()
(herdado do método da superclasseModel.all()
) - Um objeto
GqlQuery
para consultas no estilo GQL
Veja no exemplo a seguir como retornar os nomes de todos os tipos de entidade em um aplicativo:
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 namespace
Se o aplicativo opta pela API Namespaces, é possível utilizar uma consulta de namespace para encontrar todos os namespaces usados nas entidades do aplicativo. Isso permite que você execute atividades, como funções administrativas, em vários namespaces.
As consultas de namespace retornam entidades do tipo especial __namespace__
, cujo nome
de chave é o nome de um namespace. Uma exceção é o namespace padrão
designado pela string vazia ""
: como a string vazia não é um nome de chave válido,
esse namespace é identificado pelo ID numérico 1
. As consultas desse
tipo são compatíveis apenas com a filtragem de intervalos na pseudopropriedade especial
__key__
, cujo valor é a chave da entidade. Os resultados podem ser classificados por
valor __key__
crescente (mas não decrescente). Como as entidades __namespace__
não têm propriedades, as consultas apenas de chaves e sem chave retornam as mesmas
informações.
As entidades de namespace são instâncias da classe de modelo
google.appengine.ext.db.metadata.Namespace
. A propriedade de string
namespace_name
,
calculada a partir da chave da entidade, retorna o nome do namespace correspondente.
Caso a chave tenha um ID numérico 1
, a propriedade retornará a string vazia. Para
facilitar a consulta, o modelo Namespace
fornece os seguintes métodos de classe:
Namespace.key_for_namespace()
cria uma chave__namespace__
com um nome de namespace.Namespace.key_to_namespace()
retorna o nome do namespace correspondente a uma determinada chave__namespace__
.
Como exemplo, veja a implementação da função auxiliar
get_namespaces()
, que retorna uma lista
com os nomes de todos os namespace de um aplicativo (ou aqueles no
intervalo entre dois nomes especificados, start
e 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 tipo
Consultas de tipo retornam entidades do tipo __kind__
, cujo nome de chave é o nome de
um tipo de entidade. As consultas desse tipo são implicitamente restritas ao namespace atual
e são compatíveis apenas com a filtragem para intervalos acima da
pseudopropriedade __key__
. Os resultados podem ser classificados por valor __key__
crescente
(mas não decrescente). Como as entidades __kind__
não têm propriedades, as consultas apenas
de chave e sem chave retornam as mesmas informações.
As entidades de tipo são instâncias da classe de modelo
google.appengine.ext.db.metadata.Kind
.
A propriedade de string kind_name
,
calculada a partir da chave da entidade, retorna o nome do tipo de entidade
correspondente. Para facilitar a consulta, o modelo Kind
fornece os seguintes métodos de
classe:
Kind.key_for_kind()
cria uma chave__kind__
a partir de um nome do tipo.Kind.key_to_kind()
retorna o nome do tipo correspondente a uma determinada chave__kind__
.
Como exemplo, veja a implementação da função auxiliar
get_kinds()
, que retorna uma lista
com os nomes de todos os tipos de entidade de um aplicativo (ou aqueles no
intervalo entre dois nomes especificados, start
e 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]
O exemplo a seguir imprime todos os tipos com nomes que começam com uma 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 propriedade
As consultas de propriedade retornam entidades do tipo __property__
que denotam as
propriedades associadas a um tipo de entidade (se essas
propriedades estão definidas ou não no modelo do tipo). A
entidade que representa a propriedade P do tipo K é criada da seguinte forma:
- A chave da entidade tem o tipo
__property__
e o nome de chave P. - A chave da entidade pai tem o tipo
__kind__
e o nome da chave K.
Entidades de propriedade são instâncias da classe de modelo
google.appengine.ext.db.metadata.Property
. As propriedades de string
kind_name
e property_name
,
calculadas a partir da chave da entidade, retornam os nomes do tipo e da propriedade
correspondentes. O modelo Property
fornece quatro métodos de classe para simplificar a criação
e a análise de chaves __property__
:
Property.key_for_kind()
cria uma chave pai__kind__
para as chaves__property__
de um tipo de entidade especificado.Property.key_for_property()
cria uma chave__property__
para um tipo e uma propriedade especificados.Property.key_to_kind()
retorna o nome do tipo associado a uma chave__property__
.Property.key_to_property()
retorna o nome da propriedade associado a uma chave__property__
(ouNone
se a chave especificar apenas um tipo).
O exemplo a seguir ilustra esses 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"
O comportamento de uma consulta de propriedade depende do fato de ela ser uma consulta apenas de chaves ou apenas sem chave (representação de propriedade), conforme detalhado nas subseções a seguir.
Consultas de propriedade: somente com chaves
As consultas de propriedade somente com chaves retornam uma chave para cada propriedade indexada de um tipo de entidade especificado. As propriedades não indexadas não estão incluídas. O exemplo abaixo imprime os nomes de todos os tipos de entidade do aplicativo, bem como as propriedades associadas com cada um deles:
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))
As consultas desse tipo são restritas implicitamente ao namespace atual e
à filtragem de suporte apenas para intervalos na pseudopropriedade __key__
, em que as
chaves denotam entidades __kind__
ou __property__
. Os resultados podem ser
classificados por valor __key__
crescente (mas não decrescente). A filtragem é aplicada
a pares de propriedade-tipo, classificados primeiro por tipo e depois por propriedade.
Por exemplo, suponha que você tenha uma entidade com estas propriedades:
- tipo
Account
com as propriedadesbalance
company
- tipo
Employee
com as propriedadesname
ssn
- tipo
Invoice
com as propriedadesdate
amount
- tipo
Manager
com as propriedadesname
title
- tipo
Product
com as propriedadesdescription
price
A consulta para retornar os dados da propriedade tem esta aparência:
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))
A consulta acima apresenta o seguinte resultado:
Employee: ssn
Invoice: date
Invoice: amount
Manager: name
Os resultados não incluem a propriedade name
do tipo Employee
e a propriedade title
do tipo Manager
, nem quaisquer propriedades dos tipos
Account
e Product
, porque estão fora do intervalo especificado para a
consulta.
As consultas de propriedade também são compatíveis com a filtragem de ancestrais em uma chave __kind__
ou
__property__
para limitar os resultados da consulta a um único tipo ou propriedade. Você
pode usar isso, por exemplo, para ver as propriedades associadas a um determinado
tipo de entidade, como no exemplo a seguir:
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 propriedade: somente sem chaves (representação de propriedade)
As consultas de propriedade somente sem chaves, conhecidas como consultas de representação de propriedade, retornam mais informações sobre as representações usadas por cada par de propriedade-tipo. As propriedades não indexadas não estão incluídas. A entidade retornada para a propriedade
P do tipo K tem a mesma chave de uma
consulta apenas de chaves,
junto com uma propriedade property_representation
adicional que retorna as
representações da propriedade. O valor dessa propriedade é uma instância da classe
StringListProperty
que contém uma string para cada representação da
propriedade P encontrada em qualquer entidade do tipo K.
Representações e classes de propriedades não são iguais. Várias classes de propriedades podem ser mapeadas para a mesma representação. Por exemplo,
StringProperty
e
PhoneNumberProperty
usam a representação STRING
.
Veja na tabela a seguir o mapeamento das classes de propriedades para as respectivas representações:
Classe de propriedade | Representação |
---|---|
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 |
Listar a representação do elemento |
StringListProperty |
Listar a representação do elemento |
Como exemplo, veja a implementação da função auxiliar
get_representations_of_kind()
,
que retorna um dicionário com as representações de todas as
propriedades indexadas de um aplicativo (ou aquelas no intervalo entre dois nomes
especificados, start
e end
) associados a um determinado tipo de entidade. O dicionário mapeia o nome de cada propriedade e cria uma lista das representações dessa propriedade.
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