SyntaxHighlighter

2012年10月14日日曜日

Google App Engine Python NDB を使ってみた。(2)

【NDBエンティティとキー】

Datastoreに格納されているオブジェクト(エンティティ)はndb.Modelのインスタンスで、アプリケーションではndb.Modelのサブクラスとして定義する。
エンティティはkeyで識別され、アプリケーションのDatastore内でユニークとなる。

・概要


以下のようなエンティティクラスを定義する。
それぞれのエンティティはアプリケーション内のDatastoreで一意のキーで識別される。
キーがカインドと識別子を構成しているのが最も簡単なフォームである。
普通はカインドの名前はモデルクラス名と同じにする。上記の例ではAccount
だが、method _get_kind()を上書きすることで、名前を変える事も出来る。
識別子は、アプリケーションまたはデータストアで自動的に生成された整数の数字IDか割り当てられたいずれかのキー名の文字列である。
エンティティのキーは他のキーを親のキーとして示す事が出来る。これを”エンティティのキーの親”と呼ぶ。よくエンティティーの親とも呼ばれる。
コンテキストに依存することはエンティティのキーの親を意味する事か、エンティティはキーのキーを持っている事になる。ルートのエンティティーを持っていないエンティティはそれは親のエンティティで再起的にそれは先祖である。
エンティティはデータストアにいるが、階層の構成はファイルシステムの階層と同様である。エンティティの連続処理はルートのエンティティから始まって、親から子へと続く。
特定のエンティティにつながる、そのエンティティの祖先パスを構成している。

完全な一意キーは、つまり連続するカインド識別子を自身のエンティティーまで指定したその先祖キーである。
Keyクラスのコンストラクターは連続するカインドと識別子を指定すすことを許可している。その返却値のキーがそのエンティティに対応するキーとなる。
下記の例はメッセージのリビジョンを示すもので、rev_keyが所有者に所属している事になる。
上記例で注意が必要なのがリストの最後で2という数字を使っているがこれは特殊で数字キーを使うことはできるが、これは少しトリッキーになる。詳細は数値キーを使うを参照。

ルートエンティティ用としては下記のよう先祖パスは空で、自身のカインドと識別子のみで構成されている。
代わり以下のようにモデルクラスを直接代入できる。
下記例では指定しているキーはすべて等価である。

・エンティティの作成


モデルクラスのコンストラクターを呼び出すことでエンティティを作成する事が出来る。
プロパティの設定はkeyword argumentsで指定する。
ここで作成したオブジェクトはput()メソッドを呼びだす事によりデータストアへ格納さる。返却値はkeyとなっている。
代わりに以下のようにプロパティを直接指定できる。
以下のようにpopulate()を使うこともできる。

また、プロパティのタイプはいろいろなタイプが使えるがタイプチェックが行われる。
下記の例だと、StringPropertyとIntegerPropertyである。


・キーからエンティティの取得


エンティティーのキーを与えれば、データストアからエンティティを取得できる。
キーのメソッドであるkind()とidはそれぞれカインドと識別子を返却する。
parent()メソッドはキーの親のエンティティを返却する。
また、URLに埋め込むためにキーをエンコードした形で取得できる。
この生成方法ではagVoZWxsb3IPCxIHQWNjb3VudBiZiwIMのようなキーを返却するが、そこからエンティティも取得できる。

注意:URL-safeは暗号化されない。以下のように簡単に複合化できてしまうので注意。
なので、e-mailアドレスとかは暗号化したもので使用する。


・エンティティの更新


エンティティの更新はデータストアから取得したエンティティの編集を行い、データストアに戻す。
この場合のput()の返却値は同じなので無視してよい。


・エンティティの削除


エンティティが必要なくなったらデータストアから削除する。keyのdelete()メソッドで
削除できる。
このメソッドは常にNoneを返却する。


・複数のキー、エンティティを処理する。


get(),put()はRPC呼び出しを行っているので、ループを使って処理するしかなく非効率だったが、下記のメソッドで速くできる。


【Expandoモデル】


時々事前にプロパティを指定したくないときがある。その場合に特別なクラスExpandoを使う。Expandoは、割り当てられた任意の属性(限り、それはアンダースコアで始まらないように)がデータストアに保存されるように、そのエンティティの動作を変更する。たとえば、以下のように。
このデータストアへの書き込みはfooプロパティはInteger値で1barプロパティはString値'blah'でtagsプロパティはString値の繰り返しで'exp','and','oh'となっている。プロパティはインデックスされそれを_propertiesで参照することが出来る。
データストアから値を取得する事によって作成されたExpandoはデータストアに保存されたすべてのプロパティーとプロパティー値を持っている。
アプリケーションはExpandoのサブクラスとして定義して前もってプロパティを持つ事もできる。
上記の例ではnameプロパティはSandyでageはNone、動的プロパティlocationは'SF'となる。
Expandoのサブクラスに_default_indexed = Falseを指定することでインデックスから外すことができる。
_default_indexedをExpandoエンティティにセットすることができる。この場合は事後に指定したプロパティすべてに適用される。
他の便利なテクニックとしてはクエリに動的プロパティを使う事ができる。
ただ、プロパティを持っていない場合もあるので、下記のように指定する。


【モデルのフック】


NDBは軽量なフック機構を提供している。フックを利用することで、形式の処理を実現する事ができる。例としてはModelはget()のあとで様々な処理をするが、それを同期的にまたは非同期、複数など特別なメソッドを作れる。以下の例では、フックで様々な取得方法を提供している。
フックは下記で便利
・クエリーのキャッシュ
・ユーザー毎のデータストア監視
・データストアへのトリガー
もし非同期APIをフックとして使う場合はcheck_result()かget_result()かTaskletによるyieldingメソッドを呼びだすことがトリガーになる。
事後フックはRPCが成功していることは確認しない。失敗しても実行される。
すべての事後フックはFuture属性を持つ。このFutureオブジェクトは動作の結果を保持している。get_result()メソッドを呼びだせば、結果を取得できる。フックが呼び出された時点でFutureは完了しているので、get_result()でブロックされない事を確認できる。
事前フックで例外が発生すると、その場所をとってから、要求を防ぐことになる。
フックは_asyncメソッド内でトリガされるが、事前にRPCフックでtasklets.Returnを上げることによって、RPCを先取りすることはできない。


【数値キーを使う】


キーはカインドとIDの直列だが、アプリケーションとネームスペースないで、一意のキーを持っているか確認したくなる。アプリケーションがID指定なしでエンティティを作成すると、自動で数値キーが割り当てられる。アプリケーションが手動でID(数値の)を取り出すと、データストアは自動でIDを生成するが、既に使用されているIDを選択する場合がある。
回避するためには、アプリケーションが予約した数値の範囲を使用するようにするとよい。
(数値IDを使わなければ問題ない)
下記のように予約IDの範囲を指定できる。下記は100個のIDを割り当てている。
親キーと一緒に指定もできる。
キーの取り出し方は下記の通りで、first, lastの範囲でキーが割り当てられている。
これらのキーはデータストアの内部的なID生成で既に割り当てられていないIDである事は保証していない。また将来的に生成されるIDも同様である。 しかし、allocate_ids()が返すIDは、データストア内で存在するかどうかは確認しない。 代わりに下記のように最大値からID割り当てることもできる。 このフォームはN以下のIDを確実に返す。返却されるfirstとlastは予約されたIDの範囲を示す。 アプリケーションはトランザクション内でallocate_ids()を呼ぶことができない。 まあ、数値IDはあんまり使わない方が良さそう(感想)
次回は、プロパティについて。

0 件のコメント:

コメントを投稿