Note: Developers building new applications are strongly encouraged to use the NDB Client Library, which has several benefits compared to this client library, such as automatic entity caching via the Memcache API. If you are currently using the older DB Client Library, read the DB to NDB Migration Guide
The PolyModel class is the superclass for data model definitions that can themselves be superclasses for other data model definitions. A query produced from a PolyModel class can have results that are instances of the class or any of its subclasses.
PolyModel
is provided by the google.appengine.ext.db.polymodel
module.
PolyModel is a subclass of Model, and inherits its class and instance methods from that class. The PolyModel class overrides several of Model's methods, but does not introduce any new interface elements.
Introduction
It is often useful to define data models as a classification hierarchy, much like how an object database can define one class of objects as a sub-class of another. Such a database can perform queries on objects of the parent class, and include objects of the sub-class in the results. The App Engine datastore does not support this kind of query natively, but you can implement it using a mechanism included with the Python SDK, the PolyModel
class.
A model class derived from PolyModel
can be the base class for other model classes. Queries created for these classes using the all()
and gql()
methods know to include instances of subclasses in the results.
Subclasses can define new properties not present on parent classes. However, subclasses cannot override property definitions of parent classes. (Doing so results in a DuplicateProperty
error.)
For reference, here is the simple example from Entities and Models. Notice that the PolyModel
class is provided by the package google.appengine.ext.db.polymodel
.
from google.appengine.ext import db from google.appengine.ext.db import polymodel class Contact(polymodel.PolyModel): phone_number = db.PhoneNumberProperty() address = db.PostalAddressProperty() class Person(Contact): first_name = db.StringProperty() last_name = db.StringProperty() mobile_number = db.PhoneNumberProperty() class Company(Contact): name = db.StringProperty() fax_number = db.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.all(): # Returns both p and c. # ... for person in Person.all(): # Returns only p. # ...
Polymorphism is not a native feature of the datastore. Instead, polymorphism is implemented in the PolyModel
class itself. All entities created from PolyModel
subclasses are stored in the datastore with the same kind, which is the name of the root class (e.g. Animal
). Each object stores its class hierarchy as a multi-valued property of the entity named 'class'
. When the app creates a query using a PolyModel
class's all()
or gql()
method, the query includes a filter on the 'class'
property that limits the results to entities created from the class or any subclass.
Because PolyModel
uses a property of the entity to store class information, indexes for polymorphic queries must accommodate the 'class'
property. The implied filter is an equality filter, and can be combined with other equality filters and inequality filters on other properties.
Note: PolyModel uses just the names of the classes in the 'class'
property, not full paths. It's possible to create class hierarchies with multiple nodes of the same name, such as A
→ B
and A
→ C
→ B
. A query for one will return entities of both. Similarly, queries for A
→ B
→ C
and A
→ C
→ B
are functionally identical. It's best to avoid creating a single class hierarchy with multiple nodes of the same name.
PolyModel
does not support overriding property model definitions in subclasses. If a subclass tries to redefine a property that is defined on a superclass, the class definition raises a DuplicatePropertyError
.
PolyModel
supports multiple inheritance, including inheriting from multiple classes that share a superclass ("diamond" inheritance). A class cannot inherit from two classes that each define a property model definition for the same property (this would raise a DuplicatePropertyError
). However, a class can inherit from two classes that inherit the same property model definition from the same superclass.
PolyModel
does not support dynamic properties, like Expando does. There is not an equivalent of PolyModel
for Expando
.
Constructor
The constructor of the PolyModel class is defined as follows:
- class PolyModel(parent=None, key_name=None, **kwds)
-
A model class that can be a superclass to other model classes, and whose queries can include instances of subclasses as results. Like Model, the PolyModel class must be subclassed to define the kind of the data entities.
PolyModel is a subclass of Model, and inherits or overrides its methods.
Arguments
- parent
- The Model instance or Key instance for the entity that is the new entity's parent.
- key_name
-
The name for the new entity. The name becomes part of the primary key. If
None
, a system-generated ID is used for the key.The value for key_name must not start with a number, and must not be of the form
__*__
. If your application uses user-submitted data as datastore entity key names (such as an email address), the application should sanitize the value first, such as by prefixing it with a known string like "key:", to meet these requirements.A
key_name
is stored as a Unicode string, withstr
values converted as ASCII text. - **kwds
- Initial values for the instance's properties, as keyword arguments. Each name corresponds with an attribute of the new instance, and must correspond with fixed properties defined in the PolyModel class.
Class Methods
In addition to the class methods defined by the Model class, the PolyModel class provides the following class methods:
- PolyModel.class_key()
-
Returns the name of the class and the names of all parent classes for the class, as a tuple.
- PolyModel.class_name()
-
Returns the name of the class. A class can override this method if the name of the Python class changes, but entities should continue using the original class name.