注: 新しいアプリケーションを作成する際は、NDB クライアント ライブラリを使用することを強くおすすめします。NDB クライアント ライブラリには、Memcache API によるエンティティの自動キャッシュをはじめ、このクライアント ライブラリにはないメリットがあります。古い DB クライアント ライブラリを現在使用している場合は、DB から NDB への移行ガイドをお読みください。
PolyModel クラスは、自身が他のデータモデル定義のスーパークラスになることができるデータモデル定義のスーパークラスです。PolyModel クラスから生成されたクエリの結果には、クラスやそのサブクラスのインスタンスを含めることができます。
PolyModel
は google.appengine.ext.db.polymodel
モジュールによって提供されます。
PolyModel は Model のサブクラスであり、Model クラスのクラスメソッドとインスタンス メソッドを継承します。PolyModel クラスは Model クラスのメソッドのいくつかをオーバーライドしますが、新しいインターフェース要素は導入しません。
はじめに
オブジェクト データベースでオブジェクトの 1 つのクラスを別のオブジェクトのサブクラスとして定義できるのと同じように、データモデルを分類階層として定義すると便利なことがよくあります。このようなデータベースでは、親クラスのオブジェクトに対してクエリを実行し、クエリ結果にサブクラスのオブジェクトを含めることができます。App Engine データストアではこのようなクエリをネイティブにサポートしていませんが、Python SDK に同梱されている PolyModel
クラスというメカニズムを使用して実装できます。
PolyModel
から派生したモデルクラスは、他のモデルクラスの基底クラスとして使用できます。これらの派生モデルクラスに対し、all()
メソッドと gql()
メソッドを使用してクエリを作成すると、そのクエリの結果にはサブクラスのインスタンスが含まれます。
サブクラスでは、親クラスにはない新しいプロパティを定義できます。ただし、サブクラスで親クラスのプロパティ定義をオーバーライドすることはできません(オーバーライドすると、DuplicateProperty
エラーが発生します)。
参考として、以下にエンティティとモデルによる単純な例を示します。PolyModel
クラスは google.appengine.ext.db.polymodel
パッケージで提供されることに注意してください。
from google.appengine.ext import db from google.appengine.ext.db import polymodel class Contact(polymodel.PolyModel): phone_number = db.PhoneNumberProperty() address = db.PostalAddressProperty() class Person(Contact): first_name = db.StringProperty() last_name = db.StringProperty() mobile_number = db.PhoneNumberProperty() class Company(Contact): name = db.StringProperty() fax_number = db.PhoneNumberProperty() p = Person(phone_number='1-206-555-9234', address='123 First Ave., Seattle, WA, 98101', first_name='Alfred', last_name='Smith', mobile_number='1-206-555-0117') p.put() c = Company(phone_number='1-503-555-9123', address='P.O. Box 98765, Salem, OR, 97301', name='Data Solutions, LLC', fax_number='1-503-555-6622') c.put() for contact in Contact.all(): # Returns both p and c. # ... for person in Person.all(): # Returns only p. # ...
ポリモーフィズムはデータストアのネイティブ機能ではなく、PolyModel
クラス自体に実装されます。PolyModel
サブクラスから作成されたエンティティはすべて、同じ種類でデータストアに保管され、その種類(たとえば、Animal
)がルートクラスの名前になります。各オブジェクトはそのクラス階層を、'class'
というエンティティの多値プロパティとして保管します。アプリで PolyModel
クラスの all()
メソッドまたは gql()
メソッドを使用したクエリを作成すると、そのクエリには 'class'
プロパティに対するフィルタが組み込まれ、このクラスまたはそのサブクラスから作成されたエンティティに結果が絞り込まれます。
PolyModel
ではエンティティのプロパティを使用してクラス情報を格納することから、ポリモーフィック クエリのインデックスは 'class'
プロパティに対応する必要があります。暗黙に組み込まれるフィルタは等式フィルタであるため、別のプロパティに対する他の等式フィルタや不等式フィルタと組み合わせることができます。
注: PolyModel では、'class'
プロパティでクラスのフルパスではなく、クラス名のみを使用します。したがって、同じ名前を持つ複数のノードからなるクラス階層が作成される可能性があります(たとえば、A
→ B
と A
→ C
→ B
)。いずれかのノードに対するクエリは、両方のノードのエンティティを返します。同様に、A
→ B
→ C
に対するクエリと A
→ C
→ B
に対するクエリは、機能的にはまったく変わりません。同じ名前を持つ複数のノードからなる単一のクラス階層は作成しないようにするのが最善です。
PolyModel
では、サブクラスでのプロパティ モデル定義のオーバーライドをサポートしていません。サブクラスがスーパークラスで定義されているプロパティを再定義しようとすると、クラス定義で DuplicatePropertyError
が発生します。
PolyModel
では複数の継承をサポートしているため、スーパークラスを共有する複数のクラスから継承することもできます(「菱形」継承)。1 つのクラスが、同じプロパティに対してそれぞれにプロパティ モデルを定義している 2 つのクラスを継承することはできません(その場合、DuplicatePropertyError
が発生します)。ただし、同じスーパークラスから同じプロパティ モデル定義を継承する 2 つのクラスを継承することはできます。
PolyModel
では、Expando とは異なり、動的プロパティをサポートしていません。PolyModel
に相当する Expando
はありません。
コンストラクタ
PolyModel クラスのコンストラクタは次のように定義されます。
- class PolyModel(parent=None, key_name=None, **kwds)
-
他のモデルクラスのスーパークラスになることができるモデルクラスのクエリでは、結果にサブクラスのインスタンスを含めることができます。Model と同様に、PolyModel クラスはサブクラス化してデータ エンティティの種類を定義する必要があります。
PolyModel は Model クラスのサブクラスであり、Model クラスのメソッドを継承またはオーバーライドします。
引数
- parent
- 新しいエンティティの親エンティティの Model インスタンスまたは Key インスタンス。
- key_name
-
新しいエンティティの名前。この名前は主キーの一部となります。
None
の場合、システムが生成した ID がキーとして使用されます。key_name の値を数字で始めることはできません。また、
__*__
の形式にすることもできません。アプリケーションでユーザーが送信したデータ(メールアドレスなど)をデータストアのエンティティ キー名として使用する場合、これらの要件を満たすために、最初にデータの前に既知の文字列(「key:」など)を追加するなどして値をサニタイズする必要があります。key_name
は、str
の値を ASCII テキストに変換した Unicode 文字列として保存されます。 - **kwds
- キーワード引数として使用するインスタンスのプロパティの初期値。それぞれの名前が新しいインスタンスの属性に対応し、PolyModel クラスで定義された固定プロパティに対応している必要があります。
クラスメソッド
Model クラスで定義されているクラスメソッドに加え、PolyModel クラスには次のクラスメソッドがあります。
- PolyModel.class_key()
-
クラスの名前と、クラスの親クラスすべての名前をタプルとして返します。
- PolyModel.class_name()
-
クラスの名前を返します。Python クラスの名前が変更される場合、クラスでこのメソッドをオーバーライドできますが、エンティティでは引き続き元のクラス名を使用する必要があります。