Property
クラスは、サブクラス化するように設計されています。ただし、通常は既存の Property
サブクラスをサブクラス化するほうが簡単です。
特別な Property
属性にはすべて、一般公開の対象とみなされる属性であっても、名前の先頭にアンダーバーが配置されます。これは、StructuredProperty
がアンダーバーが配置されていない属性の名前空間を使用して、ネストされた Property
名を参照するためです。これは、サブプロパティにクエリを指定するうえで非常に重要です。
Property
クラスとこのクラスの事前定義されたサブクラスを使用すると、積み重ね(スタック)可能な検証と変換 API を使用してサブクラス化を行うことができます。このためには、次のように用語を定義する必要があります。
- ユーザー値。エンティティの標準属性を使用したアプリケーション コードによって設定、アクセスが可能な値です。
- ベース値。データストアに対してシリアル化または非シリアル化された値です。
ユーザー値とシリアル化可能な値間の特定の変換を実装する Property
サブクラスは、_to_base_type()
と _from_base_type()
の 2 つのメソッドを実装する必要があります。これらのメソッドからメソッドの 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()
メソッドが呼び出され、(例外が発生しなければ)値がエンティティに格納されます。エンティティをデータストアに書き込む際は _to_base_type()
メソッドが呼び出され、値が文字列に変換されます。この値は、ベースクラス StringProperty
でシリアル化されます。エンティティが Datastore から読み取られると、これとは逆の順番でイベントが発生します。 StringProperty
クラスと Property
クラスは、ともに文字列のシリアル化とシリアル化の解除、デフォルトの設定、プロパティの繰り返し値の処理など、その他の詳細情報の処理を行います。
この例で不等式を使用するには(<、<=、>、>= を使用したクエリなど)、さらに作業が必要になります。 次の例では、整数の最大サイズを設定し、固定長の文字列として値を格納しています。
これはプロパティ コンストラクタにビット数を渡す必要がある点を除いて、LongIntegerProperty
と同じ方法で使用できます(例 BoundedLongIntegerProperty(1024)
)。
他のプロパティ タイプも同じようにサブクラス化できます。
構造化データを保存する際にも同じアプローチが利用できます。
日付範囲を表す FuzzyDate
Python クラスがある場合は、次のようにフィールド first
と last
を使用して日付範囲の開始日と終了日を格納します。
...
StructuredProperty
から派生する FuzzyDateProperty
を作成できます。ただし、後者はプレーンな古い Python クラスで機能しません。Model
サブクラスが必要です。中間表現として Model サブクラスを定義します。
次に、StructuredProperty
のサブクラスを作成して FuzzyDateModel
として modelclass 引数をハードコードし、FuzzyDate
と FuzzyDateModel
間の変換を行う _to_base_type()
メソッドと _from_base_type()
メソッドを定義します。
アプリケーションでは、このクラスを次のように利用できます。
...
FuzzyDateProperty
の値として、FuzzyDate
オブジェクトに加えてプレーンな date
オブジェクトを受け入れるとします。この処理を行うため、_validate()
メソッドを次のように変更します。
上の例のように FuzzyDateProperty._validate()
を使用すると、次のように FuzzyDateProperty
をサブクラス化することもできます。
MaybeFuzzyDateProperty
フィールドに値を割り当てると、MaybeFuzzyDateProperty._validate()
と FuzzyDateProperty._validate()
の両方がこの順番で呼び出されます。_to_base_type()
と _from_base_type()
についても同様です。スーパークラスとサブクラスのメソッドが暗黙的に統合されます。継承した動作を制御する目的で super
を使用しないでください。
この 3 つのメソッドの場合、相互作用は希薄であり、super
は想定どおりに動作しません。