Remarque : Les développeurs qui créent des applications sont vivement encouragés à utiliser la bibliothèque cliente NDB qui présente plusieurs avantages supplémentaires par rapport à cette bibliothèque cliente, tels que la mise en cache automatique des entités via l'API Memcache. Si vous utilisez actuellement l'ancienne bibliothèque cliente DB, consultez le guide de migration de DB vers NDB.
Datastore offre un accès automatisé à certaines de ses métadonnées de façon à permettre la métaprogrammation, à mettre en œuvre des fonctions d'administration de backend, à simplifier la mise en cache cohérente et à des fins semblables. Par exemple, vous pouvez vous en servir pour créer une visionneuse Datastore personnalisée pour votre application. Les métadonnées disponibles incluent des informations sur les groupes d'entités, les espaces de noms, les genres d'entités et les propriétés qu'emploie l'application, ainsi que sur les représentations de chaque propriété.
Le tableau de bord Datastore de la console Google Cloud fournit également des métadonnées sur votre application, mais les données affichées diffèrent nettement à certains égards de celles renvoyées par ces fonctions.
- Actualisation. La lecture des métadonnées à l'aide de l'API permet d'obtenir les données actuelles, tandis que les données du tableau de bord ne sont actualisées qu'une fois par jour.
- Contenus. Certaines métadonnées du tableau de bord ne sont pas disponibles via les API, et vice versa.
- Rapidité. Les opérations d'obtention et de requête de métadonnées sont facturées de la même manière que celles appliquées à un datastore. L'exécution de requêtes de métadonnées qui extraient des informations sur les espaces de noms, les genres et les propriétés est habituellement lente. En règle générale, partez du principe qu'une requête de métadonnées renvoyant N entités va prendre à peu près le même temps que N requêtes ordinaires renvoyant chacune une seule entité. De plus, les requêtes de représentation de propriété (requêtes de propriété ne contenant pas que des clés) sont plus lentes que les requêtes de propriété ne contenant que des clés. L'obtention des métadonnées d'un groupe d'entités est légèrement plus rapide que pour les métadonnées d'une entité standard.
Fonctions de l'outil d'aide
Les fonctions suivantes permettent d'obtenir des informations de métadonnées :
get_entity_group_version()
obtient un numéro de version pour un groupe d'entités. Cette information est utile pour savoir si une entité du groupe a changé depuis la dernière demande de numéro de version.get_namespaces()
renvoie une liste contenant les noms de tous les espaces de noms d'une application ou ceux d'une plage spécifiée.get_kinds()
renvoie une liste contenant les noms de tous les genres d'entités d'une application ou ceux d'une plage spécifiée.get_properties_of_kind()
renvoie une liste contenant les noms de toutes les propriétés indexées d'une application (ou de celles d'une plage spécifiée) associées à un genre d'entité donné. Les propriétés non indexées ne sont pas incluses.get_representations_of_kind()
renvoie un dictionnaire contenant les représentations de toutes les propriétés indexées d'une application ou de celles d'une plage spécifiée associées à un genre d'entité donné. Le dictionnaire mappe le nom de chaque propriété à une liste des représentations de cette propriété. Les propriétés non indexées ne sont pas incluses.
Métadonnées de groupe d'entités
Cloud Datastore donne accès à la "version" d'un groupe d'entités, un nombre strictement positif dont l'augmentation est garantie à chaque modification du groupe d'entités.
L'exemple suivant illustre comment obtenir la version d'un groupe d'entités :
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)
Ancien comportement
Lorsque vous conservez l'ancien comportement d'une version de groupe d'entités, celle-ci n'augmente que si des modifications sont apportées au groupe d'entités. L'ancien comportement des métadonnées d'un groupe d'entités peut servir, par exemple, à assurer la cohérence des résultats mis en cache générés par une requête ascendante complexe au sein d'un groupe d'entités.
Cet exemple met en cache les résultats (un nombre de résultats) d'une requête et sollicite l'ancien comportement des versions de groupe d'entités pour utiliser la valeur mise en cache si elle est actuelle :
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()
peut renvoyer None
pour un groupe d'entités sur lequel aucune écriture n'a été effectuée.
Les versions de groupe d'entités s'obtiennent en appelant get()
sur une pseudo-entité spéciale contenant une propriété __version__
. Pour en savoir plus, consultez la documentation de référence sur EntityGroup.
Requêtes de métadonnées
Si les fonctions de l'outil d'aide décrites dans la section précédente ne répondent pas à vos besoins, vous pouvez émettre des requêtes de métadonnées plus élaborées ou plus flexibles en utilisant une requête de métadonnées explicite. Avec Python, les classes de modèle de ces requêtes sont définies dans le package google.appengine.ext.db.metadata
. Ces modèles fournissent des genres d'entités spéciaux réservés aux requêtes de métadonnées :
Classe de modèle | Genre d'entité |
---|---|
Namespace |
__namespace__ |
Kind |
__kind__ |
Property |
__property__ |
Ces modèles et ces genres n'entreront pas en conflit avec d'autres du même nom qui peuvent déjà exister dans votre application. En exécutant des requêtes sur ces genres spéciaux, vous pouvez récupérer des entités contenant les métadonnées souhaitées.
Les entités renvoyées par les requêtes de métadonnées sont générées de manière dynamique, en fonction de l'état actuel de Datastore. Bien que vous puissiez créer des instances locales des classes de modèle Namespace
, Kind
, ou Property
, toute tentative de stockage dans Datastore échouera, générant une exception BadRequestError
.
Vous pouvez émettre des requêtes de métadonnées à l'aide d'un objet de requête appartenant à l'une des deux classes suivantes :
- Un objet
Query
renvoyé par la méthode de classeNamespace.all()
,Kind.all()
ouProperty.all()
(héritée de la méthode de superclasseModel.all()
) - Un objet
GqlQuery
pour les requêtes de style GQL
L'exemple suivant renvoie les noms de tous les genres d'entités qui se trouvent dans une application :
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
Requêtes d'espace de noms
Si votre application utilise l'API Namespaces, vous pouvez exécuter une requête d'espace de noms pour rechercher tous les espaces de noms utilisés dans les entités de l'application. Cela vous permet par exemple d'effectuer des tâches d'administration sur plusieurs espaces de noms.
Les requêtes d'espace de noms renvoient des entités du genre spécial __namespace__
, dont le nom de clé correspond à celui d'un espace de noms. (L'espace de noms par défaut désigné par la chaîne vide ""
constitue une exception : comme la chaîne vide n'est pas un nom de clé valide, cet espace de noms est associé à l'ID numérique 1
à la place.) Les requêtes de ce type n'acceptent le filtrage que pour les plages définies pour la pseudo-propriété spéciale __key__
, dont la valeur est la clé de l'entité. Les résultats peuvent être triés par ordre croissant (mais pas décroissant) de valeur __key__
. Comme les entités __namespace__
n'ont pas de propriétés, les requêtes renvoient les mêmes informations, qu'elles contiennent exclusivement des clés ou non.
Les entités d'espace de noms sont des instances de la classe de modèle google.appengine.ext.db.metadata.Namespace
. La propriété de chaîne namespace_name
, calculée à partir de la clé de l'entité, renvoie le nom de l'espace de noms correspondant.
(Si l'identifiant numérique de la clé est 1
, la propriété renvoie une chaîne vide.) Pour faciliter les requêtes, le modèle Namespace
fournit les méthodes de classe suivantes :
Namespace.key_for_namespace()
crée une clé__namespace__
à partir d'un nom d'espace de noms.Namespace.key_to_namespace()
renvoie le nom d'espace de noms correspondant à une clé__namespace__
donnée.
Voici un exemple de mise en œuvre de la fonction get_namespaces()
de l'outil d'aide. Celle-ci renvoie une liste contenant les noms de tous les espaces de noms d'une application (ou ceux compris dans la plage entre les deux noms spécifiés, start
et 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]
Requêtes de genre
Les requêtes de genre renvoient des entités du genre __kind__
, dont le nom de clé correspond à celui d'un genre d'entité. Les requêtes de ce type sont implicitement restreintes à l'espace de noms actuel et n'acceptent le filtrage que pour les plages définies pour la pseudo-propriété __key__
. Les résultats peuvent être triés par ordre croissant (mais pas décroissant) de valeur __key__
. Comme les entités __kind__
n'ont pas de propriétés, les requêtes renvoient les mêmes informations, qu'elles contiennent exclusivement des clés ou non.
Les entités de genre sont des instances de la classe de modèle google.appengine.ext.db.metadata.Kind
.
La propriété de chaîne kind_name
, calculée à partir de la clé de l'entité, renvoie le nom du genre d'entité correspondant. Pour faciliter les requêtes, le modèle Kind
fournit les méthodes de classe suivantes :
Kind.key_for_kind()
crée une clé__kind__
à partir d'un nom de genre.Kind.key_to_kind()
renvoie le nom de genre correspondant à une clé__kind__
donnée.
Voici un exemple de mise en œuvre de la fonction get_kinds()
de l'outil d'aide. Celle-ci renvoie une liste contenant les noms de tous les genres d'entités d'une application (ou ceux compris dans la plage entre les deux noms spécifiés, start
et 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]
L'exemple suivant renvoie tous les genres dont le nom commence par une lettre minuscule :
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
Requêtes de propriété
Les requêtes de propriété renvoient des entités du genre __property__
indiquant les propriétés associées à un genre d'entité (que ces propriétés soient ou non actuellement définies dans le modèle du genre). L'entité représentant la propriété P du genre K est conçue comme suit :
- La clé de l'entité est du genre
__property__
et porte le nom p. - La clé de l'entité parente est du genre
__kind__
et porte le nom K.
Les entités de propriété sont des instances de la classe de modèle google.appengine.ext.db.metadata.Property
. Les propriétés de chaîne kind_name
et property_name
, calculées à partir de la clé de l'entité, renvoient les noms du genre et de la propriété correspondants. Le modèle Property
propose quatre méthodes de classe pour simplifier la création et l'examen des clés __property__
:
Property.key_for_kind()
crée une clé__kind__
parente pour les clés__property__
d'un genre d'entité spécifié.Property.key_for_property()
crée une clé__property__
pour un genre et une propriété spécifiés.Property.key_to_kind()
renvoie le nom de genre associé à une clé__property__
.Property.key_to_property()
renvoie le nom de la propriété associée à une clé__property__
(ouNone
si la clé spécifie uniquement un genre).
L'exemple suivant illustre ces méthodes :
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"
Une requête de propriété se comporte différemment selon qu'il s'agit d'une requête ne contenant que des clés ou d'une requête ne contenant pas que des clés (représentation de propriétés), comme détaillé dans les sous-sections ci-dessous.
Requêtes de propriété ne contenant que des clés
Les requêtes de propriété ne contenant que des clés renvoient une clé pour chaque propriété indexée d'un genre d'entité spécifié. (Les propriétés non indexées ne sont pas incluses.) L'exemple suivant imprime les noms de tous les genres d'entités d'une application et les propriétés associées à chacun d'eux :
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))
Les requêtes de ce type sont implicitement restreintes à l'espace de noms actuel et n'acceptent le filtrage que pour les plages définies pour la pseudo-propriété __key__
, lorsque les clés désignent des entités __kind__
ou __property__
. Les résultats peuvent être triés par ordre croissant (mais pas décroissant) de valeur __key__
. Le filtrage est appliqué aux paires genre-propriété, triées d'abord par genre et ensuite par propriété. Supposons, par exemple, qu'une entité ait les propriétés suivantes :
- genre
Account
avec des propriétésbalance
company
- genre
Employee
avec des propriétésname
ssn
- genre
Invoice
avec des propriétésdate
amount
- genre
Manager
avec des propriétésname
title
- genre
Product
avec des propriétésdescription
price
La requête qui doit renvoyer les données de propriété ressemblerait à ceci :
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 requête ci-dessus renverrait les éléments suivants :
Employee: ssn
Invoice: date
Invoice: amount
Manager: name
Notez que les résultats n'incluent pas la propriété name
du genre Employee
, ni la propriété title
du genre Manager
, ni aucune propriété du genre Account
ou Product
, car elles ne sont pas comprises dans la plage spécifiée pour la requête.
Les requêtes de propriété acceptent également le filtrage en fonction des ancêtres sur une clé __kind__
ou __property__
, afin de limiter les résultats à un seul genre ou à une seule propriété. Vous pouvez, par exemple, suivre cette approche pour obtenir les propriétés associées à un genre d'entité donné, comme dans l'exemple suivant :
get_properties_of_kind()
de l'outil d'aide)
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]
Requêtes de propriété ne contenant pas que des clés (représentation de propriété)
Les requêtes de propriété ne contenant pas que des clés, appelées requêtes de représentation de propriété, renvoient des informations supplémentaires sur les représentations utilisées par chaque paire genre-propriété. (Les propriétés non indexées ne sont pas incluses.) L'entité renvoyée pour une propriété P de genre K a la même clé qu'une requête correspondante ne contenant que des clés, mais également une propriété property_representation
supplémentaire qui renvoie les représentations de la propriété. La valeur de cette propriété correspond à une instance de la classe StringListProperty
contenant une chaîne pour chaque représentation de la propriété P trouvée dans une entité de genre K.
Notez que les représentations ne sont pas identiques aux classes de propriétés. Plusieurs classes de propriétés peuvent correspondre à la même représentation. Par exemple, les classes StringProperty
et PhoneNumberProperty
utilisent toutes deux la représentation STRING
.
Le tableau suivant établit une correspondance entre les classes de propriétés et leurs représentations :
Classe de propriété | Représentation |
---|---|
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 |
Représentation d'élément de liste |
StringListProperty |
Représentation d'élément de liste |
Voici un exemple de mise en œuvre de la fonction get_representations_of_kind()
de l'outil d'aide. Celle-ci renvoie un dictionnaire contenant les représentations pour toutes les propriétés indexées d'une application (ou celles comprises dans la plage entre les deux noms spécifiés, start
et end
), associées à un genre d'entité donné. Le dictionnaire mappe le nom de chaque propriété sur une liste des représentations de cette propriété :
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