Property
類別專門用於劃分子類別。不過,通常較容易將現有的 Property
子類別設為子類別。
所有特殊 Property
屬性 (包括視為「公開」的屬性) 的名稱開頭都是底線。這是因為 StructuredProperty
會使用非底線屬性命名空間,參照巢狀 Property
名稱;這對於指定子屬性的查詢至關重要。
Property
類別及其預先定義的子類別,可讓您使用可組合 (或可堆疊) 的驗證和轉換 API 建立子類別。這些分類作業需要一些術語定義:
- 使用者值是應用程式程式碼使用實體標準屬性設定及存取的值。
- 基本值是會序列化至 Datastore 和從 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
之間轉換:
應用程式可能會以下列方式來使用此類別:
...
假設您想接受純 date
物件,以及 FuzzyDate
物件做為 FuzzyDateProperty
的值。如要這樣做,請按照下列方式修改 _validate()
方法:
您可以改為將 FuzzyDateProperty
子類別化,如下所示 (假設 FuzzyDateProperty._validate()
如上所示)。
當您將值指派給 MaybeFuzzyDateProperty
欄位時,系統會依序叫用 MaybeFuzzyDateProperty._validate()
和 FuzzyDateProperty._validate()
。_to_base_type()
和 _from_base_type()
也是如此:父類別和子類別中的方法會隱含合併。(請勿使用 super
控制此項目的繼承行為。
這三種方法的互動方式很微妙,使用 super
無法達到您想要的效果)。