Property
类可以进行子类化。
但是,对现有的 Property
子类进行子类化通常更容易。
所有特殊的 Property
特性 (attribute),即使是公共特性,都具有以下划线开头的名称。这是因为 StructuredProperty
使用非下划线特性命名空间来表示嵌套的 Property
名称;在指定针对子属性 (property) 的查询时,这样做很有必要。
Property
类及其预定义子类允许使用可组合(或可堆叠)验证和转换 API 进行子类化。为说明这些概念,我们需要一些术语定义:
- 用户值是由使用实体标准特性的应用代码设置和访问的值。
- 基值是从 Datastore 中进行序列化和反序列化的值。
假设某个 Property
子类要实现用户值与可序列化值之间的特定转换,就应该实施两种方法:_to_base_type()
和 _from_base_type()
。这些方法不应该调用其 super()
方法。这就是可组合(或可堆叠)API 的含义。
API 支持具有更复杂的用户值-基值转换的堆叠类:用户值到基值的转换是从较复杂变为较不复杂,而基值到用户值的转换是从较不复杂变为较复杂。例如,请看 BlobProperty
、TextProperty
和 StringProperty
之间的关系。TextProperty
继承自 BlobProperty
;其代码非常简单,因为它只继承了它需要的大部分行为。
除了 _to_base_type()
和 _from_base_type()
之外,_validate()
方法也是一个可组合的 API。
验证 API 区分宽松和严格的用户值。宽松值集是严格值集的超集。_validate()
方法采用宽松值,并在必需要时将其转换为严格值。这意味着在设置属性值时,接受宽松值,而在获取属性值时,仅返回严格值。如果不需要转换,_validate()
可能会返回 None。如果参数超出了接受的宽松值集,则 _validate()
应引发异常,可能是 TypeError
或 datastore_errors.BadValueError
。
_validate()
、_to_base_type()
和 _from_base_type()
不需要处理:
None
:将不调用它们,并返回None
(如果它们返回 None,则表示值不需要转换)。- 重复值:基础架构负责为具有重复值的每个列表项调用
_from_base_type()
或_to_base_type()
。 - 区分用户值与基值:基础架构通过调用可组合的 API 来处理此问题。
- 比较:比较运算依靠其操作数调用
_to_base_type()
。 - 区分用户值和基值:基础架构保证
_from_base_type()
将通过(未封装)基值调用,而_to_base_type()
将通过用户值调用。
例如,假设您需要存储非常长的整数。
标准 IntegerProperty
仅支持(有符号的)64 位整数。您的属性可能会将更长的整数存储为字符串;让属性类处理转换会很好。
使用属性类的应用可能像下面这样
...
...
...
...
这看起来简单明了。它还演示了一些标准属性选项的使用(默认、重复);在使用 LongIntegerProperty
时,您将会很高兴,因为不必编写任何“样板文件”来解决这种问题。定义另一个属性的子类更简单,例如:
当您设置实体的属性值时(例如 ent.abc = 42
),系统会调用您的 _validate()
方法,并且会在实体上存储该值(如果这样做不会引发异常的话)。当您将实体写入 Datastore 时,系统会调用您的 _to_base_type()
方法,将该值转换为字符串。然后,该值由基类 StringProperty
进行序列化。再从 Datastore 中读取该实体时,会发生一连串逆序事件。StringProperty
和 Property
类配合处理其他细节,例如序列化和反序列化字符串、设置默认值以及处理重复的属性值。
在本示例中,使用不等式(即使用 <、<=、>、>= 进行查询)需要执行更多工作。 以下示例实现方法强制使用整数大小上限,并将值存储为固定长度的字符串:
其使用方式与 LongIntegerProperty
相同,只是您必须将位数传递给属性构造函数,例如 BoundedLongIntegerProperty(1024)
。
您可以使用同样的方式对其他属性类型进行子类化。
该方法也适用于存储结构化数据。
假设您有一个表示日期范围的 FuzzyDate
Python 类;它使用字段 first
和 last
来存储日期范围的开头和结尾:
...
您可以创建一个派生自 StructuredProperty
的 FuzzyDateProperty
。遗憾的是,后者无法处理普通的旧 Python 类;它需要一个 Model
子类。因此,将 Model 子类定义为中间表示形式:
接下来,构造 StructuredProperty
的子类,用于将 modelclass 参数硬编码为 FuzzyDateModel
,并定义 _to_base_type()
和 _from_base_type()
方法,以便在 FuzzyDate
和 FuzzyDateModel
之间进行转换:
应用可能会像下面这样使用此类:
...
假设除了 FuzzyDate
对象之外,您还要接受普通的 date
对象作为 FuzzyDateProperty
的值。为此,请按如下所示修改 _validate()
方法:
您可以按如下所示对 FuzzyDateProperty
进行子类化(假设 FuzzyDateProperty._validate()
如上所述)。
当您为 MaybeFuzzyDateProperty
字段分配值时,系统会调用 MaybeFuzzyDateProperty._validate()
和 FuzzyDateProperty._validate()
(按该顺序)。这同样适用于 _to_base_type()
和 _from_base_type()
:父类和子类中的方法是隐式组合的。(不要使用 super
来控制继承的行为。
对于这三种方法,需要的交互都很少,而 super
并不能满足您的需求。)