Nota: os programadores que criam novas aplicações são fortemente aconselhados a usar a biblioteca de cliente NDB, que tem várias vantagens em comparação com esta biblioteca de cliente, como o armazenamento em cache automático de entidades através da API Memcache. Se estiver a usar atualmente a biblioteca cliente DB mais antiga, leia o guia de migração de DB para NDB
O Datastore oferece acesso programático a alguns dos respetivos metadados para suportar a metaprogramação, implementar funções administrativas de back-end, simplificar o armazenamento em cache consistente e fins semelhantes. Pode usá-lo, por exemplo, para criar um visualizador do Datastore personalizado para a sua aplicação. Os metadados disponíveis incluem informações sobre os grupos de entidades, os espaços de nomes, os tipos de entidades e as propriedades que a sua aplicação usa, bem como as representações de propriedades para cada propriedade.
O painel de controlo do Datastore na consola também fornece alguns metadados sobre a sua aplicação, mas os dados apresentados aí diferem em alguns aspetos importantes dos devolvidos por estas funções. Google Cloud
- Atualidade. A leitura de metadados através da API obtém dados atuais, enquanto os dados no painel de controlo são atualizados apenas uma vez por dia.
- Conteúdo. Alguns metadados no painel de controlo não estão disponíveis através das APIs. O inverso também é verdadeiro.
- Velocidade. As consultas e as obtenções de metadados são faturadas da mesma forma que as consultas e as obtenções do Datastore. As consultas de metadados que obtêm informações sobre espaços de nomes, tipos e propriedades são, geralmente, lentas de executar. Como regra geral, espere que uma consulta de metadados que devolve N entidades demore aproximadamente o mesmo tempo que N consultas comuns, cada uma das quais devolve uma única entidade. Além disso, as consultas de representação de propriedades (consultas de propriedades apenas com chaves) são mais lentas do que as consultas de propriedades apenas com chaves. As obtenções de metadados de metadados de grupos de entidades são ligeiramente mais rápidas do que a obtenção de uma entidade normal.
Funções auxiliares
As seguintes funções obtêm informações de metadados:
get_entity_group_version()
recebe um número de versão para um grupo de entidades. Isto é útil para saber se alguma entidade no grupo foi alterada desde a última vez que recebeu o número de versão.get_namespaces()
devolve uma lista com os nomes de todos os espaços de nomes de uma aplicação ou os de um intervalo especificado.get_kinds()
devolve uma lista com os nomes de todos os tipos de entidades de uma aplicação ou os de um intervalo especificado.get_properties_of_kind()
devolve uma lista com os nomes de todas as propriedades indexadas de uma aplicação (ou as que se encontram num intervalo especificado) associadas a um determinado tipo de entidade. As propriedades não indexadas não estão incluídas.get_representations_of_kind()
devolve um dicionário que contém as representações de todas as propriedades indexadas de uma aplicação ou as que se encontram num intervalo especificado associado a um determinado tipo de entidade. O dicionário mapeia o nome de cada propriedade para uma lista das representações dessa propriedade. As propriedades não indexadas não estão incluídas.
Metadados do grupo de entidades
O Cloud Datastore fornece acesso à "versão" de um grupo de entidades, um número estritamente positivo que tem a garantia de aumentar em todas as alterações ao grupo de entidades.
O exemplo seguinte mostra como obter 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 antigo
No comportamento da versão do grupo de entidades antigo, a versão do grupo de entidades só aumenta quando são feitas alterações ao grupo de entidades. O comportamento dos metadados do grupo de entidades antigo pode ser usado, por exemplo, para manter uma cache consistente de uma consulta de antepassados complexa num grupo de entidades.
Este exemplo coloca em cache os resultados da consulta (uma contagem de resultados correspondentes) e usa o comportamento antigo das versões do grupo de entidades para usar o valor em cache se estiver atualizado:
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 devolver None
para um grupo de entidades que nunca foi escrito.
As versões do grupo de entidades são obtidas chamando get()
numa pseudoentidade especial que contém uma propriedade __version__
. Consulte a documentação de referência sobre EntityGroup para ver detalhes.
Consultas de metadados
Se as funções auxiliares descritas na secção anterior não satisfizerem as suas necessidades, pode emitir pedidos de metadados mais elaborados ou flexíveis com uma consulta de metadados explícita. Em Python, as classes de modelos para essas consultas estão definidas no pacote google.appengine.ext.db.metadata
. Estes modelos fornecem tipos de entidades especiais que estão reservados para consultas de metadados:
Classe do modelo | Tipo de entidade |
---|---|
Namespace |
__namespace__ |
Kind |
__kind__ |
Property |
__property__ |
Estes modelos e tipos não entram em conflito com outros do mesmo nome que já possam existir na sua aplicação. Ao consultar estes tipos especiais, pode obter entidades que contêm os metadados pretendidos.
As entidades devolvidas pelas consultas de metadados são geradas dinamicamente com base no estado atual do Datastore. Embora possa criar instâncias locais das classes de modelos Namespace
, Kind
ou Property
, qualquer tentativa de as armazenar no Datastore falha com uma exceção BadRequestError
.
Pode emitir consultas de metadados através de um objeto de consulta pertencente a uma de duas classes:
- Um objeto
Query
devolvido 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
O exemplo seguinte devolve os nomes de todos os tipos de entidades numa aplicação:
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 espaços de nomes
Se a sua aplicação usar a API Namespaces, pode usar uma consulta de espaço de nomes para encontrar todos os espaços de nomes usados nas entidades da aplicação. Isto permite-lhe realizar atividades como funções administrativas em vários espaços de nomes.
As consultas de espaço de nomes devolvem entidades do tipo especial __namespace__
cuja chave
é o nome de um espaço de nomes. (Uma exceção é o espaço de nomes predefinido
designado pela string vazia ""
: uma vez que a string vazia não é um nome
de chave válido, este espaço de nomes é indexado com o ID numérico 1
.) As consultas deste tipo suportam a filtragem apenas para intervalos sobre a pseudopropriedade especial __key__
, cujo valor é a chave da entidade. Os resultados podem ser ordenados pelo valor __key__
ascendente (mas não descendente). Uma vez que as entidades __namespace__
não têm propriedades, as consultas apenas com chaves e sem chaves devolvem as mesmas informações.
As entidades de espaço de nomes 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, devolve o nome do espaço de nomes correspondente.
(Se a chave tiver o ID numérico 1
, a propriedade devolve a string vazia.) Para facilitar as consultas, o modelo Namespace
fornece os seguintes métodos de classe:
Namespace.key_for_namespace()
cria uma chave__namespace__
a partir de um nome de espaço de nomes.Namespace.key_to_namespace()
devolve o nome do espaço de nomes correspondente a uma determinada chave__namespace__
.
Por exemplo, aqui está a implementação da função auxiliar
get_namespaces()
, que devolve uma lista
com os nomes de todos os espaços de nomes de uma aplicação (ou os que estão 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
As consultas de tipo devolvem entidades do tipo __kind__
cujo nome da chave é o nome de um tipo de entidade. As consultas deste tipo estão implicitamente restritas ao espaço de nomes atual e suportam a filtragem apenas para intervalos sobre a pseudopropriedade __key__
. Os resultados podem ser ordenados por valor de __key__
ascendente (mas não descendente). Uma vez que as entidades __kind__
não têm propriedades, as consultas apenas com chaves e sem chaves devolvem as mesmas informações.
As entidades do tipo são instâncias da classe do modelo google.appengine.ext.db.metadata.Kind
.
A propriedade de string kind_name
, calculada a partir da chave da entidade, devolve o nome do tipo de entidade correspondente. Para facilitar as consultas, o modelo Kind
fornece os seguintes métodos de classe:
Kind.key_for_kind()
cria uma chave__kind__
a partir de um nome de tipo.Kind.key_to_kind()
devolve o nome do tipo correspondente a uma determinada chave__kind__
.
Por exemplo, aqui está a implementação da função auxiliar get_kinds()
, que devolve uma lista com os nomes de todos os tipos de entidades de uma aplicação (ou os que estão 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 seguinte imprime todos os tipos cujos nomes começam por 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 propriedades
As consultas de propriedades devolvem entidades do tipo __property__
que denotam as propriedades associadas a um tipo de entidade (independentemente de essas propriedades estarem ou não definidas atualmente 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 da chave P. - A chave da entidade principal tem o tipo
__kind__
e o nome da chave K.
As entidades de propriedades 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, devolvem os nomes do tipo e da propriedade correspondentes. O modelo Property
oferece quatro métodos de classe para simplificar a criação e a análise de chaves __property__
:
Property.key_for_kind()
Cria uma chave__kind__
principal para__property__
chaves 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()
devolve o nome do tipo associado a uma chave__property__
.Property.key_to_property()
devolve o nome da propriedade associado a uma chave__property__
(ouNone
se a chave especificar apenas um tipo).
O exemplo seguinte ilustra estes 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 de ser uma consulta apenas com chaves ou sem ser apenas com chaves (representação de propriedade), conforme detalhado nas subsecções abaixo.
Consultas de propriedades: apenas chaves
As consultas de propriedades apenas com chaves devolvem 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 seguinte imprime os nomes de todos os tipos de entidades de uma aplicação e as propriedades associadas a cada um:
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 deste tipo estão implicitamente restritas ao espaço de nomes atual e
suportam a filtragem apenas para intervalos sobre a pseudopropriedade __key__
, em que as
chaves denotam entidades __kind__
ou __property__
. Os resultados podem ser ordenados pelo valor __key__
ascendente (mas não descendente). A filtragem é aplicada a pares de tipo-propriedade, ordenados primeiro por tipo e, em segundo lugar, por propriedade. Por exemplo, suponhamos que tem uma entidade com estas propriedades:
- kind
Account
com propriedadesbalance
company
- kind
Employee
com propriedadesname
ssn
- kind
Invoice
com propriedadesdate
amount
- kind
Manager
com propriedadesname
title
- kind
Product
com propriedadesdescription
price
A consulta para devolver os dados da propriedade teria o seguinte aspeto:
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 devolveria o seguinte:
Employee: ssn
Invoice: date
Invoice: amount
Manager: name
Repare que 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 propriedades também suportam a filtragem de antepassados numa chave __kind__
ou __property__
, para limitar os resultados da consulta a um único tipo ou propriedade. Pode usar isto, por exemplo, para obter as propriedades associadas a um determinado tipo de entidade, como no exemplo seguinte:
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 propriedades: não apenas chaves (representação de propriedades)
As consultas de propriedades que não são apenas de chaves, conhecidas como consultas de representação de propriedades, devolvem informações adicionais sobre as representações usadas por cada par de tipo-propriedade. (As propriedades não indexadas não estão incluídas.) A entidade devolvida para a propriedade
P do tipo K tem a mesma chave que para uma
consulta apenas de chaves,
juntamente com uma propriedade property_representation
adicional que devolve as
representações da propriedade. O valor desta 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.
Tenha em atenção que as representações não são o mesmo que as classes de propriedades. Várias classes de propriedades podem ser mapeadas para a mesma representação. (Por exemplo,
StringProperty
e
PhoneNumberProperty
usam a representação STRING
.)
A tabela seguinte faz o mapeamento das classes de propriedades para as respetivas 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 |
Representação do elemento de lista |
StringListProperty |
Representação do elemento de lista |
Por exemplo, aqui está a implementação da função auxiliar
get_representations_of_kind()
,
que devolve um dicionário que contém as representações de todas as propriedades indexadas de uma aplicação (ou as que estão no intervalo entre dois nomes especificados, start
e end
) associadas a um determinado tipo de entidade. O dicionário mapeia o nome de cada propriedade para 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