エンティティ モデルの作成

エンティティ用のモデルクラスを作成する必要があります。作成する方法は次の 2 つです。

  • エンティティ プロパティを定義するモデルクラスを作成する
  • 事前にエンティティを定義しない expando モデルクラスを作成する

このドキュメントでは、それぞれの種類のモデルクラスを作成する方法について説明します。また、アプリケーションが、それぞれの get() の前など、特定の種類のオペレーションの前または後に特定のコードを実行できるように、モデルフックを作成する方法についても説明します。

プロパティを使用したモデルクラスの作成

エンティティを作成する前に、1 つまたは複数のエンティティ プロパティを定義するモデルクラスを作成する必要があります。次に例を示します。

from google.appengine.ext import ndb
...
class Account(ndb.Model):
    username = ndb.StringProperty()
    userid = ndb.IntegerProperty()
    email = ndb.StringProperty()

それぞれの Account エンティティにユーザー名、ユーザー ID、メールのプロパティを含めます。

プロパティの型の完全なリストについては、エンティティ プロパティ リファレンスをご覧ください。

Expando モデルクラスの作成

事前にプロパティを定義するモデルクラスを必ずしも使用する必要はありません。Expando という特殊なモデル サブクラスを使用すると、エンティティの動作が変化し、割り当てられたすべての属性がデータストアに保存されます。なお、このような属性をアンダースコアで始めることはできません。

次に Expando モデルの作成方法を示します。

class Mine(ndb.Expando):
    pass
...
e = Mine()
e.foo = 1
e.bar = 'blah'
e.tags = ['exp', 'and', 'oh']
e.put()

これにより、整数値 1 を持つ foo プロパティ、文字列値 'blah'bar プロパティ、さらに文字列値 'exp''and''oh' の反復プロパティ tag を含むエンティティがデータストアに書き込まれます。プロパティにはインデックスが付き、エンティティの _properties 属性を使って照会できます。

return e._properties
# {
#     'foo': GenericProperty('foo'),
#     'bar': GenericProperty('bar'),
#     'tags': GenericProperty('tags', repeated=True)
# }

データストアから値を取得することで作成される Expando には、データストアに保存されたすべてのプロパティ値に関するプロパティが含まれます。

アプリケーションは、事前定義されたプロパティを Expando サブクラスに次のように追加できます。

class FlexEmployee(ndb.Expando):
    name = ndb.StringProperty()
    age = ndb.IntegerProperty()
...
employee = FlexEmployee(name='Sandy', location='SF')

これにより、employee に対して、値が 'Sandy'name 属性、値が Noneage 属性、値が 'SF' の動的属性 location が付与されます。

プロパティがインデックス化されていない Expando サブクラスを作成するには、サブクラス定義で _default_indexed = False を設定します。

class Specialized(ndb.Expando):
    _default_indexed = False
...
e = Specialized(foo='a', bar=['b'])
return e._properties
# {
#     'foo': GenericProperty('foo', indexed=False),
#     'bar': GenericProperty('bar', indexed=False, repeated=True)
# }

Expando エンティティで _default_indexed を設定することもできます。この場合、設定後に割り当てられるすべてのプロパティに影響が及びます。

他の便利な手法として、Expando という種類に対して動的プロパティをクエリできます。次のようなクエリがあるとします。

FlexEmployee.query(FlexEmployee.location == 'SF')

これは機能しません。location プロパティ用のプロパティ オブジェクトがクラスに存在しないためです。代わりに GenericProperty を使用し、動的プロパティ用にクラス Expando で次のように使用します。

FlexEmployee.query(ndb.GenericProperty('location') == 'SF')

モデルフックの使用

NDB には、シンプルなフック メカニズムが用意されています。フックを定義すると、アプリケーションは特定の種類のオペレーションの前または後に特定のコードを実行できます。たとえば、Model でそれぞれの get() の前にいずれかの関数を実行できます。

フック関数は、該当するメソッドの同期バージョン、非同期バージョン、マルチ バージョンを使用する際に実行されます。たとえば「get 前(プリ get)」フックは get()get_async()get_multi() のすべてに適用されます。それぞれのフックには RPC 前(プリ RPC)および RPC 後(ポスト RPC)バージョンがあります。

フックは次の場合に役立つ可能性があります。

  • クエリをキャッシュする
  • ユーザーごとのデータストア アクティビティを監査する
  • データベース トリガーを模倣する

次の例は、フック関数を定義する方法を示しています。

from google.appengine.ext import ndb
...
class Friend(ndb.Model):
    name = ndb.StringProperty()

    def _pre_put_hook(self):
        _notify('Gee wiz I have a new friend!')

    @classmethod
    def _post_delete_hook(cls, key, future):
        _notify('I have found occasion to rethink our friendship.')
...
f = Friend()
f.name = 'Carole King'
f.put()  # _pre_put_hook is called
fut = f.key.delete_async()  # _post_delete_hook not yet called
fut.get_result()  # _post_delete_hook is called

非同期 API でポストフックを使用する場合、check_result()get_result() を呼び出すか、(タスクレット内で)非同期メソッドの future を生成することで、フックがトリガーされます。ポストフックは、RPC が成功したかどうかを検査しません。失敗した場合でも、フックは実行されます。

すべてのポストフックには、呼び出し署名の末尾に Future 引数があります。この Future オブジェクトは、アクションの結果を保持します。この Future に対して get_result() を呼び出すことで、結果を取得できます。フックが呼び出される時点までには Future が完了しているため、get_result() は決してブロックしません。

プリフック中に例外が発生すると、リクエストが発生しなくなります。フックは <var>*</var>_async メソッド内でトリガーされますが、プリ RPC フックで tasklets.Return を起動して RPC をプリエンプトすることはできません。

次のステップ