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 無法達到您想要的效果)。