Documentation sur les propriétés d'entité

Firestore en mode Datastore (Datastore) est compatible avec divers types de données pour les valeurs de propriété, parmi lesquels :

  • Entiers
  • Nombres à virgule flottante
  • Chaînes
  • Dates
  • Données binaires

Pour obtenir la liste complète des types, consultez la section Propriétés et types de valeurs.

Propriétés et types de valeurs

Les valeurs de données associées à une entité consistent en une ou plusieurs propriétés. Chaque propriété a un nom, et une ou plusieurs valeurs. Une propriété peut avoir des valeurs de plus d'un type, et deux entités peuvent avoir des valeurs de types différents pour la même propriété. Les propriétés peuvent être indexées ou non indexées (les requêtes qui ordonnent ou filtrent une propriété P ignorent les entités pour lesquelles P n'est pas indexée.) Une entité peut avoir 20 000 propriétés indexées au maximum.

Les types de valeurs suivants sont acceptés :

Lorsqu'une requête implique une propriété avec des valeurs de types mixtes, Datastore utilise un ordre déterministe basé sur les représentations internes :

  1. Valeurs Null
  2. Nombres à virgule fixe
    • Entiers
    • Dates et heures
  3. Valeurs booléennes
  4. Séquences d'octets
    • Chaîne Unicode
    • Clés Blobstore
  5. Nombres à virgule flottante
  6. Clés Datastore

Étant donné que les chaînes de texte et d'octets longues ne sont pas indexées, aucun tri n'est défini pour celles-ci.

Types de propriétés

NDB est compatible avec les types de propriétés suivants :

Type de propriétéDescription
IntegerProperty Entier signé de 64 bits
FloatProperty Nombre à virgule flottante avec deux décimales
BooleanProperty Valeur booléenne
StringProperty Chaîne Unicode, jusqu'à 1 500 octets, indexée
TextProperty Chaîne Unicode, longueur illimitée, non indexée
BlobProperty Chaîne d'octets non interprétée :
Si vous définissez indexed=True : jusqu'à 1 500 octets, indexée ;
Si indexed est False (valeur par défaut) : longueur illimitée, non indexée.
Argument de mot clé facultatif : compressed.
DateTimeProperty Date et heure (voir Propriétés de date et d'heure)
DateProperty Date (voir Propriétés de date et d'heure)
TimeProperty Heure (voir Propriétés de date et d'heure)
GeoPtProperty Emplacement géographique. Il s'agit d'un objet ndb.GeoPt doté des attributs lat et lon. Tous deux sont des nombres à virgule flottante. Vous pouvez construire un objet avec deux nombres à virgule flottante, par exemple ndb.GeoPt(52.37, 4.88), ou avec une chaîne ndb.GeoPt("52.37, 4.88") (il s'agit en fait de la même classe que db.GeoPt).
KeyProperty Clé Datastore
Argument de mot clé facultatif : kind=kind, pour exiger que les clés attribuées à cette propriété aient toujours le genre indiqué. Il peut s'agir d'une chaîne ou d'une sous-classe Model.
BlobKeyProperty Clé Blobstore
Correspond à BlobReferenceProperty dans l'ancienne API DB, mais sa valeur est un élément BlobKey au lieu d'un élément BlobInfo. Vous pouvez construire un élément BlobInfo à partir de cette valeur à l'aide de BlobInfo(blobkey).
UserProperty Objet utilisateur
StructuredProperty Inclut un genre de modèle dans un autre, par valeur (voir Propriétés structurées).
LocalStructuredProperty Semblable à StructuredProperty, mais la représentation sur disque est un blob opaque et n'est pas indexée (voir Propriétés structurées).
Argument de mot clé facultatif : compressed.
JsonProperty La valeur est un objet Python (comme une liste, un dictionnaire ou une chaîne) sérialisable à l'aide du module json de Python. Datastore stocke la sérialisation JSON sous forme de blob. Non indexée par défaut.
Argument de mot clé facultatif : compressed.
PickleProperty La valeur est un objet Python (comme une liste, un dictionnaire ou une chaîne) sérialisable à l'aide du protocole de pickle de Python. Datastore stocke la sérialisation avec pickle sous forme de blob. Non indexée par défaut.
Argument de mot clé facultatif : compressed.
GenericProperty Valeur générique
Utilisée principalement par la classe Expando, mais également utilisable de manière explicite. Elle peut être de type int, long, float, bool, str, unicode, datetime, Key, BlobKey, GeoPt, User ou None.
ComputedProperty Valeur calculée à partir d'autres propriétés par une fonction définie par l'utilisateur (voir Propriétés calculées).

Certaines de ces propriétés possèdent un argument de mot clé facultatif, compressed. Si la propriété est définie sur compressed=True, ses données sont compressées avec gzip sur le disque. Cela prend moins d'espace, mais l'encodage et le décodage lors des opérations d'écriture et de lecture nécessitent l'utilisation du processeur.

La compression et la décompression sont "paresseuses". Une valeur de propriété compressée ne sera décompressée que la première fois que vous y accédez. Si vous lisez une entité contenant une valeur de propriété compressée et que vous la réécrivez sans accéder à la propriété compressée, elle ne sera ni décompressée, ni compressée. Le cache en contexte participe également à ce schéma paresseux, mais Memcache stocke toujours la valeur compressée pour les propriétés compressées.

En raison du temps CPU supplémentaire nécessaire à la compression, il est généralement préférable de n'utiliser des propriétés compressées que si les données sont trop volumineuses sans elles. N'oubliez pas que la compression basée sur gzip est en général inefficace pour les images et autres données multimédias, car ces formats sont déjà compressés à l'aide d'un algorithme de compression qui leur est propre (par exemple, JPEG pour les images).

Options des propriétés

La plupart des types de propriétés acceptent des arguments standards. Le premier d'entre eux est un argument positionnel facultatif spécifiant le nom Datastore de la propriété. Il permet d'attribuer à la propriété un nom différent dans Datastore de celui utilisé du côté de l'application. Cela permet généralement de réduire l'espace dans Datastore et à celui-ci d'utiliser des noms de propriété abrégés même si, dans votre code, les noms sont plus longs et plus significatifs. Par exemple :

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

Cela est particulièrement utile dans le cas des propriétés répétées pour lesquelles vous attendez un grand nombre de valeurs par entité.

De plus, la plupart des types de propriétés acceptent les arguments de mots clés suivants :

Argument Type Par défaut Description
indexed bool Généralement True Permet d'inclure la propriété dans les index Datastore. S'il est défini sur False, les valeurs ne peuvent pas être interrogées, mais les écritures sont plus rapides. Les types de propriétés ne sont pas tous compatibles avec l'indexation. La définition de indexed sur True échoue pour ceux-ci.
Les propriétés non indexées entraînent moins d'opérations en écriture que les propriétés indexées.
repeated bool False La valeur de propriété est une liste Python contenant les valeurs du type sous-jacent (voir Propriétés répétées).
Ne peut pas être combiné avec required=True ou default=True.
required bool False Une valeur doit être spécifiée pour la propriété.
default Type sous-jacent de la propriétéNone Valeur par défaut de la propriété si aucune n'est explicitement spécifiée.
choices Liste de valeurs du type sous-jacentNone Liste facultative des valeurs autorisées.
validator Fonction None

Fonction facultative permettant de valider et éventuellement de convertir la valeur.

Sera appelé avec des arguments (prop, value) et doit renvoyer la valeur (éventuellement convertie) ou générer une exception. Le fait d'appeler à nouveau la fonction sur une valeur convertie ne devrait pas modifier davantage la valeur. Par exemple, le renvoi de value.strip() ou value.lower() est acceptable, mais pas celui de value + '$'. Peut également renvoyer None, ce qui signifie qu'aucun changement n'est effectué. Voir aussi Écrire des sous-classes de propriétés

verbose_name chaîne None

Libellé HTML facultatif à utiliser dans les frameworks de formulaire Web tels que jinja2.

Propriétés répétées

Toute propriété pour laquelle repeated=True est défini devient une propriété répétée. La propriété prend une liste de valeurs du type sous-jacent plutôt qu'une seule valeur. Par exemple, la valeur d'une propriété définie avec IntegerProperty(repeated=True) est une liste d'entiers.

Datastore peut voir plusieurs valeurs pour ce type de propriétés. Un enregistrement d'index distinct est créé pour chaque valeur, ce qui a une incidence sur la sémantique des requêtes. Pour obtenir un exemple, consultez la section Interroger des propriétés répétées.

Cet exemple utilise une propriété répétée :

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()

Cela crée une entité Datastore contenant les éléments suivants :

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

Lors de l'interrogation de la propriété tags, cette entité satisfera à une requête pour 'python' ou 'ruby'.

Lors de la mise à jour d'une propriété répétée, vous pouvez lui attribuer une nouvelle liste ou modifier la liste existante. En cas d'attribution d'une nouvelle liste, les types de ses éléments sont immédiatement validés. Les types d'éléments non valides (par exemple, l'attribution de [1, 2] à art.tags ci-dessus) génèrent une exception. En cas de modification de la liste, le changement apporté n'est pas immédiatement validé. Au lieu de cela, la valeur est validée lorsque vous écrivez l'entité dans Datastore.

Datastore conserve l'ordre des éléments de la liste dans une propriété répétée. Vous pouvez ainsi attribuer un sens à leur classement.

Propriétés de date et d'heure

Trois types de propriétés permettent de stocker les valeurs liées à la date et à l'heure :

  • DateProperty
  • TimeProperty
  • DateTimeProperty

Ces propriétés prennent des valeurs appartenant aux classes correspondantes (date, time et datetime) du module datetime Python standard. Le type le plus général des trois est DateTimeProperty, qui désigne à la fois une date du calendrier et une heure de la journée. Les autres sont parfois utiles dans des cas spéciaux, où seule une date (comme une date de naissance) ou une heure (par exemple, une heure de réunion) est nécessaire. Pour des raisons techniques, DateProperty et TimeProperty sont des sous-classes de DateTimeProperty, mais vous ne devez pas compter sur cette relation d'héritage (notez qu'elle diffère des relations d'héritage existant entre les classes sous-jacentes définies par le module datetime lui-même).

Remarque : Les temps d'horloge App Engine sont toujours exprimés en temps universel coordonné (UTC). C'est important si vous utilisez la date ou l'heure actuelle (datetime.datetime.now()) en tant que valeur, ou si vous effectuez une conversion entre des objets "datetime" et des horodatages POSIX ou des tuples de temps. Toutefois, aucune information de fuseau horaire explicite n'est stockée dans Datastore. Par conséquent, en faisant preuve de prudence, vous pouvez vous en servir pour représenter les heures locales dans n'importe quel fuseau horaire, si vous utilisez l'heure actuelle ou les conversions.

Chacune de ces propriétés comporte deux options de mots clés booléens supplémentaires :

Option Description
auto_now_add Définit la propriété sur la date et l'heure actuelles lors de la création de l'entité. Vous pouvez remplacer cette propriété manuellement. Lorsque l'entité est mise à jour, la propriété ne change pas. Pour ce comportement, utilisez auto_now.
auto_now Définit la propriété sur la date et l'heure actuelles lors de la création de l'entité et à chaque mise à jour.

Ces options ne peuvent pas être combinées avec repeated=True. Dans les deux cas, la valeur par défaut est False. Si toutes deux sont définies sur True, auto_now est prioritaire. Il est possible de remplacer la valeur d'une propriété présentant l'option auto_now_add=True, mais pas celle d'une propriété présentant l'option auto_now=True. La valeur automatique n'est générée que lorsque l'entité est écrite, ce qui signifie que ces options ne fournissent pas de valeurs par défaut de manière dynamique. Ces détails diffèrent de l'ancienne API DB.

Remarque : Lorsqu'une transaction écrivant une propriété qui présente l'option auto_now_add=True échoue et fait l'objet d'une nouvelle tentative, elle réutilise la même valeur de temps que lors de la tentative d'origine plutôt que de la mettre à jour vers l'heure de la nouvelle tentative. Si la transaction échoue de manière permanente, la valeur de la propriété est toujours définie dans la copie en mémoire de l'entité.

Propriétés structurées

Vous pouvez structurer les propriétés d'un modèle. Par exemple, vous pouvez définir une classe de modèle Contact contenant une liste d'adresses, chacune avec une structure interne. Les propriétés structurées (type StructuredProperty) vous permettent d'effectuer cette opération. Exemple :

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()

Cela crée une seule entité Datastore dotée des propriétés suivantes :

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'

La relecture d'une telle entité reconstruit exactement l'entité Contact d'origine. Bien que les instances Address soient définies à l'aide de la même syntaxe que celle employée pour les classes de modèle, ce ne sont pas de véritables entités. Elles ne possèdent pas leurs propres clés dans Datastore. Elles ne peuvent pas être récupérées indépendamment de l'entité Contact à laquelle elles appartiennent. Toutefois, une application peut interroger les valeurs de leurs champs individuels. Consultez la section Filtrer par valeur de propriété structurée. Notez que Datastore considère address.type, address.street et address.city comme des tableaux parallèles, mais que la bibliothèque NDB masque cet aspect et construit la liste correspondante d'instances Address.

Vous pouvez spécifier les options de propriété habituelles pour les propriétés structurées (sauf indexed). Le nom Datastore est le deuxième argument positionnel dans ce cas (le premier étant la classe de modèle utilisée pour définir la sous-structure).

Lorsque vous n'avez pas besoin d'interroger les propriétés internes d'une sous-structure, vous pouvez utiliser une propriété structurée locale (LocalStructuredProperty). Si vous remplacez StructuredProperty par LocalStructuredProperty dans l'exemple ci-dessus, le comportement du code Python est identique, mais Datastore ne voit qu'un blob opaque pour chaque adresse. L'entité guido créée dans l'exemple serait stockée comme suit : name = 'Guido' address = <blob opaque pour {'type': 'home', 'city': 'Amsterdam'}> address = <blob opaque pour {'type': 'work', 'city': 'SF', 'street': 'Spear St'}>

L'entité sera relue correctement. Comme les propriétés de ce type sont toujours non indexées, vous ne pouvez pas interroger les valeurs d'adresse.

Remarque : Une propriété StructuredProperty dotée d'une propriété imbriquée (qu'elle soit structurée ou non) n'accepte qu'une seule couche de propriétés répétées. La propriété StructuredProperty peut être répétée ou la propriété imbriquée peut être répétée, mais pas les deux. Pour contourner le problème, vous pouvez utiliser LocalStructuredProperty, qui ne présente pas cette contrainte (mais n'autorise pas les requêtes sur ses valeurs de propriété).

Propriétés calculées

Les propriétés calculées (ComputedProperty) sont des propriétés en lecture seule dont la valeur est calculée à partir d'autres valeurs de propriété par une fonction fournie par l'application. Notez qu'une propriété calculée n'est compatible qu'avec les types acceptés par les propriétés génériques. La valeur calculée est écrite dans Datastore de sorte qu'elle puisse être interrogée et affichée dans la visionneuse Datastore, mais la valeur stockée est ignorée lorsque l'entité est relue à partir de Datastore. À la place, la valeur est recalculée en appelant la fonction chaque fois que la valeur est demandée. Exemple :

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

Cela stocke une entité avec les valeurs de propriété suivantes :

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

Si nous changeons le nom en "Nickie" et demandons la valeur de name_lower, "nickie" est renvoyé :

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

Remarque : Utilisez ComputedProperty si l'application interroge la valeur calculée. Si vous souhaitez simplement utiliser la version dérivée dans le code Python, définissez une méthode standard ou utilisez la propriété @property intégrée de Python.

Remarque : Si vous créez un modèle sans clé spécifiée manuellement et que vous comptez plutôt sur Datastore pour générer automatiquement l'ID de l'entité, alors une propriété put() ne pourra pas lire le champ d'ID la première fois que vous effectuez une opération ComputedProperty puisque celui-ci est calculé avant que l'ID ne soit généré. Si vous avez besoin d'une propriété ComputedProperty qui utilise l'ID de l'entité, vous pouvez utiliser la méthode allocate_ids pour générer un ID et une clé avec lesquels créer l'entité, afin que votre propriété ComputedProperty soit en mesure de référencer cet ID sur la première méthode "put()" de l'entité.

Propriétés de messages Google Protocol RPC

La bibliothèque Google Protocol RPC utilise des objets Message pour les données structurées. Ils peuvent représenter des requêtes RPC, des réponses ou d'autres éléments. NDB fournit une API permettant de stocker les objets Message de Google Protocol RPC en tant que propriétés d'entités. Supposons que vous définissiez une sous-classe Message :

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

Vous pouvez stocker des objets Note dans Datastore en tant que valeurs de propriété d'entité à l'aide de l'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 vous souhaitez interroger des noms de champs, ceux-ci doivent être indexés. Vous pouvez spécifier une liste de noms de champs qui seront indexés avec le paramètre indexed_fields sur MessageProperty.

MessageProperty accepte de nombreuses options de propriété, mais pas toutes. Les options compatibles sont les suivantes :

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

Les propriétés de messages ne sont pas compatibles avec l'option de propriété indexed. Vous ne pouvez pas indexer les valeurs Message. Vous pouvez toutefois indexer les champs d'un message comme décrit ci-dessus.

Les messages imbriqués (à l'aide de MessageField) fonctionnent également :

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 possède une option de propriété spéciale, protocol, qui spécifie la façon dont l'objet Message est sérialisé vers Datastore. Les valeurs sont les noms de protocoles utilisés par la classe protorpc.remote.Protocols. Les noms de protocoles acceptés sont protobuf et protojson ; la valeur par défaut est protobuf.

msgprop définit également EnumProperty, un type de propriété pouvant servir à stocker une valeur protorpc.messages.Enum. Exemple :

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 stocke la valeur sous forme d'entier. En fait, EnumProperty est une sous-classe de IntegerProperty. Par conséquent, vous pouvez renommer vos valeurs d'énumération sans avoir à modifier les entités déjà stockées, mais vous ne pouvez pas les renuméroter.

EnumProperty accepte les options de propriétés suivantes :

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

À propos des modèles d'entité NDB

Un modèle d'entité NDB peut définir des propriétés. Les propriétés d'entité sont un peu comme les membres de données des classes Python, une manière structurée de stocker des données. Elles sont aussi un peu comme les champs d'un schéma de base de données.

Pour définir un modèle de données, une application type définit une classe qui hérite de Model avec certains attributs de classe de propriété. Par exemple :


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

Ici, username, userid et email sont des propriétés de Account.

Il existe plusieurs autres types de propriétés. Certains sont pratiques pour représenter les dates et les heures, et disposent de fonctionnalités pratiques de mise à jour automatique.

Une application peut ajuster le comportement d'une propriété en spécifiant des options sur la propriété. Celles-ci peuvent faciliter la validation, définir des valeurs par défaut ou modifier l'indexation des requêtes.

Un modèle peut comporter des propriétés plus complexes. Les propriétés répétées ressemblent à des listes. Les propriétés structurées sont semblables à des objets. Les propriétés calculées en lecture seule sont définies via des fonctions, ce qui facilite la définition d'une propriété en fonction d'une ou de plusieurs autres propriétés. Les modèles Expando peuvent définir des propriétés de manière dynamique.