Property
類別專門用於劃分子類別。不過,通常會將現有的 Property
子類別做為子類別。
所有特殊 Property
屬性 (即使是視為「public」的屬性) 的名稱開頭都是底線。這是因為 StructuredProperty
使用非底線屬性命名空間來參照巢狀 Property
名稱,這對於指定子屬性查詢至關重要。
Property
類別及其預先定義的子類別可讓您使用可組合 (或可堆疊) 驗證和轉換 API 進行子類別化。這些分類作業需要一些術語定義:
- 使用者值是指應用程式程式碼使用實體上的標準屬性設定及存取的值。
- 基本值是指會序列化至資料儲存庫,並從資料儲存庫反序列化的值。
在使用者值和可序列化的值之間實作特定轉換的 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
無法達到您想要的效果)。