PolyModel 类

注意强烈建议构建新应用的开发者使用 NDB 客户端库,它与 DB 客户端库相比具有多项优势,例如可通过 Memcache API 进行自动实体缓存。如果您当前使用的是较早的 DB 客户端库,请参阅 DB 到 NDB 的迁移指南

PolyModel 类是数据模型定义的父类,这些数据模型定义本身也可以是其他数据模型定义的父类。通过 PolyModel 类生成的查询的结果可以是该类或其任意子类的实例。

PolyModelgoogle.appengine.ext.db.polymodel 模块提供。

PolyModel 是 Model 的子类,前者会继承后者的类以及该类中的实例方法。PolyModel 类会覆盖多个 Model 方法,但并不引入任何新的接口元素。

简介

将数据模型定义为分类层次结构往往很有用,这种定义方式类似于对象数据库将一类对象定义为另一类对象的子类。此类数据库可对父类对象执行查询,并在结果中包括子类的对象。App Engine 数据存储区本身不支持这类查询,但是可使用 Python SDK 包括的一种机制(PolyModel 类)来实现它。

PolyModel 派生的模型类可以作为其他模型类的基类。使用 all()gql() 方法为这些类创建的查询知道要在结果中包含子类的实例。

子类可以定义父类上不存在的新属性。不过,子类无法覆盖父类的属性定义。(这样做将导致 DuplicateProperty 错误。)

下面是实体和模型中的一个简单示例,以供参考。请注意:PolyModel 类由 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.
  # ...

多态性不是数据存储区的原生特性。但可以在 PolyModel 类本身中实现多态性。通过 PolyModel 子类创建的所有实体均以同一个种类存储在数据存储区中,种类名称即为根类的名称(例如 Animal)。每个对象都会将其类层次结构存储为名为 'class' 的实体的多值属性。当应用使用 PolyModel 类的 all()gql() 方法创建查询时,查询将包括针对 'class' 属性的过滤条件,以将结果限制为通过该类或任何子类创建的实体。

由于 PolyModel 使用实体属性存储类信息,则多态查询的索引必须包含 'class' 属性。隐含的过滤条件是等式过滤条件,可与针对其他属性的其他等式过滤条件和不等式过滤条件结合使用。

注意:PolyModel 仅会使用 'class' 属性中类的名称,而非完整路径。可以创建具有多个同名节点的类层次结构,例如 AB and ACB。对其中一个层次结构的查询将返回这两个层次结构的实体。同样,对 ABCACB 的查询在功能上是相同的。最好避免创建具有多个同名节点的单个类层次结构。

PolyModel 不支持覆盖子类中的属性模型定义。如果子类尝试重新定义已在父类中定义的属性,则该类定义将引发 DuplicatePropertyError

PolyModel 支持多重继承,包括从共用一个父类的多个类继承(“菱形”继承)。如果两个类为同一属性分别定义一个属性模型定义,则无法从这两个类中继承类(将引发 DuplicatePropertyError)。不过,如果两个类均已从同一父类中继承相同的属性模型定义,则可以从这两个类中继承类。

Expando 一样,PolyModel 不支持动态属性。对于 Expando,没有等同于 PolyModel 的类。

构造函数

PolyModel 类的构造函数定义如下:

class PolyModel(parent=None, key_name=None, **kwds)

一个模型类,可以是其他模型类的父类,并且其查询可以包含子类实例作为结果。与 Model 类似,必须对 PolyModel 类进行子类化才能定义数据实体的种类。

PolyModel 是 Model 的子类,前者继承或覆盖后者的方法。

参数

parent
新实体的父实体的 Model 实例或 Key 实例。
key_name

新实体的名称。该名称会成为主键的一部分。如果此参数为 None,则系统生成的 ID 将用于该键。

key_name 的值不得以数字开头,也不得采用 __*__ 格式。如果您的应用使用用户提交的数据(例如电子邮件地址)作为数据存储区实体键名称,则应用应首先清理该值,例如在其前面添加一个已知字符串(如“key:”),以满足这些要求。

key_name 存储为 Unicode 字符串,str 值转换为 ASCII 文本。

**kwds
实例属性的初始值,作为关键字参数。每个名称都与新实例的一个属性对应,且必须与 PolyModel 类中定义的固定属性对应。

类方法

除了 Model 类定义的类方法外,PolyModel 类还提供以下类方法:

PolyModel.class_key()

以元组的形式返回类名称以及类的所有父类的名称。

PolyModel.class_name()

返回类名称。如果 Python 类的名称发生了更改,则某个类可覆盖此方法,但实体应继续使用原来的类名称。