Classe PolyModel

Par rapport à la classe Model standard, la classe PolyModel offre davantage de flexibilité dans la définition de modèles, permettant ainsi à une application d'accepter les requêtes polymorphes. Une requête générée à partir d'une classe dérivée de PolyModel peut renvoyer des résultats constitués d'instances de la classe ou de l'une ou l'autre de ses sous-classes.

Ce type de requête est défini dans google.appengine.ext.ndb.polymodel. L'exemple ci-dessous illustre la flexibilité offerte par la classe PolyModel :

from google.appengine.ext import ndb
from google.appengine.ext.ndb import polymodel

class Contact(polymodel.PolyModel):
  phone_number = ndb.PhoneNumberProperty()
  address = ndb.PostalAddressProperty()

class Person(Contact):
  first_name = ndb.StringProperty()
  last_name = ndb.StringProperty()
  mobile_number = ndb.PhoneNumberProperty()

class Company(Contact):
  name = ndb.StringProperty()
  fax_number = ndb.PhoneNumberProperty()

p = Person(phone_number='1-206-555-9234',
           address='123 First Ave., Seattle, WA, 98101',
           first_name='Alfred',
           last_name='Smith',
           mobile_number='1-206-555-0117')
p.put()

c = Company(phone_number='1-503-555-9123',
            address='P.O. Box 98765, Salem, OR, 97301',
            name='Data Solutions, LLC',
            fax_number='1-503-555-6622')
c.put()

for contact in Contact.query():
  print 'Phone: %s\nAddress: %s\n\n' % (contact.phone_number, contact.address)

La requête Contact.query() renvoie des instances de Person et de Company. Si Contact était dérivé de Model au lieu de PolyModel, chaque classe aurait une valeur kind différente et la requête Contact.query() ne renverrait pas d'instances des sous-classes de Contact.

Si vous souhaitez ne récupérer que les instances de Person, utilisez Person.query(). Vous pouvez également utiliser Contact.query(Contact.class_ == 'Person').

En plus des méthodes Model habituelles, la méthode PolyModel propose certaines alternatives intéressantes :

  • _get_kind() : nom de la classe racine, par exemple Person._get_kind() == 'Contact'. La classe racine, "Contact" dans cet exemple, peut remplacer cette méthode de façon à utiliser un autre nom en tant que type ("kind") utilisé dans le datastore (pour toute la hiérarchie racine associée).
  • _class_name() : nom de la classe actuelle, par exemple Person._class_name() == 'Person'. Une classe feuille, "Person" dans notre exemple, peut remplacer cette méthode pour utiliser un autre nom en tant que nom de classe et dans la clé de la classe. Une classe non-feuille peut aussi remplacer cette méthode, mais attention : ses sous-classes doivent également la remplacer, sinon elles utiliseront toutes le même nom de classe et vous serez vite perdu.
  • _class_key() : liste de noms de classes indiquant la hiérarchie. Exemple : Person._class_key() == ['Contact', 'Person']. Pour les hiérarchies plus profondes, cela inclut toutes les bases entre PolyModel et la classe actuelle, y compris cette dernière, à l'exclusion du PolyModel lui-même. Identique à la valeur de la propriété class_. Son nom dans le datastore est "class".

Étant donné que le nom de la classe est utilisé dans la propriété class_ et que cette dernière est utilisée pour distinguer les sous-classes, les noms de classes (tels que renvoyés par _class_name()) doivent être uniques parmi ces sous-classes.