SyntaxHighlighter

2012年11月11日日曜日

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

プロパティーのサブクラスを書く(Writing Property Subclasses)


プロパティクラスはサブクラス化できるよう設計されている。しかし普通は既存のプロパティクラスの方が簡単にサブクラス化できる。
全ての特別なプロパティ属性とpublicと考えられる属性もアンダースコアから始まる名前を持っている。この理由はStructuredPropertyがネストされたプロパティーのアンダースコアの付いていない属性を使用するからで、これはサブプロパティにクエリを指定する為に不可欠である。

プロパティクラスと既存のサブクラスは構成可能な(またはスタッカブル)検証および変換APIを使用してサブクラス化する事が出来る。これらはいくつかの用語定義を必要とする。

・ユーザーの値(user value)は、アプリケーションが使用する標準の属性を持つエンティティに設定されるまたは、アクセスされるような値。
・基本の値(base value)はデータストアからシリアライズまたはでシリアライズされるような値。

シリアライズ可能な値とuser valueの間で特定の変換を実装する場合は二つのメソッドを実装した方がよい。to_base_type()と_from_base_type()がそれで、これらは構成可能なAPIという意味でsuper()メソッドは呼ばない方がよい。

APIは今までよりも洗練されたuser-baseな変換を行えるstacking classesをサポートしている。user-to-base変換はbase-to-user 変換でより洗練から更に洗練へ変換している間に更に洗練からより洗練へ行く。例えば、BlobProperty,TextPropertyとStringPropertyの関係や、例えば、TextPropertyはBlobPropertyを継承している。それは必要な動作のほとんどを継承しているので、そのコードは簡単なもの。
加えて、_to_base_type()と_from_base_type(),_validate()メソッドもまた、変換APIである。

バリデーションAPIはuser valuesへの緩い制約と厳密な制約とを区別する。 緩い制約の値は厳密な値の集合の上位集合である。_validate()メソッドは緩い値をとり、また必要であれば、厳密な値へ変換する。これは、プロパティの値を取得する場合に、唯一厳密な値が返されるときにプロパティー値を設定するときは、緩い制約の値が受け入れられていることを意味する。もし変換が必要ない場合は、_validate()は多分Noneを返す。もし外部からの引数が、緩い値を設定した場合は、_validate()は例外を送出すべきでTypeErrorかdatasore_error.BadValueErrorが好ましい。

_validate(),_to_base_Type(),_from_base_type()の操作が必要が無い場合:
・None:これらはNoneと一緒に呼び出せない。(また、Noneを返却する場合、それは変換が必要ない事を意味する)
・Repeated values:基盤で_from_base_type()か_to_base_typeをそれぞれのアイテム毎に呼ぶ。
・base valuesからuservaluesを区別する場合:基盤では変換APIがよびだされる。
・比較する場合:比較演算は、オペランドに_to_base_type()を呼び出す。
・base valueとuser valueを区別する場合:基盤で_from_base_type()がbase valueと一緒に呼び出される事と、_to_base_type()がuser valueと一緒に呼び出される事を保証している。

例えば、本当に長いintegerを必要とすると仮定する。標準のIntegerPropertyは64bitのintegerしかサポートしていない。長いintegerを保存する場合はStringとして格納しなくてはならない。変換を操作するpropertyクラスが良い。アプリケーションでそのプロパティを使うと恐らくこのようになるであろう。

これはシンプルで素直だ。またこのデモは標準のプロパティのオプションも使用している。
LongIntegerPropertyクラスの所有者はこれらの作業を取得する任意の定型を記述するひつようがないので、喜んでいるでしょう。他のプロパティのサブクラスを定義するのは簡単だ。下記が例

例えば、ent.abc = 42をエンティティのプロパティに設定した際_validate()メソッドが呼びだされて、値はエンティティに格納される。また、エンティティをDatastoreに格納する際は_to_base_type()が呼び出され、string値に変換する。そして、その値はStringProperyによってシリアライズされる。Datastoreからエンティティを読み戻した場合に逆算が起こる。
StringPropertyとPropertyクラスはシリアライズやデシリアライズ、デフォルト値の設定、repeated propertyの値を操作するような他の世話を一緒にする。

不等号を扱うにはさらに仕事とが必要である。下記の例では、固定長文字列として整数を格納した値の最大サイズを課している。

これは、LongIntegerPropertyと同じ方法で使用され、整数をプロパティのコンストラクターで設定されることを期待している。BoudedLongIntegerProperty(1024)のように。

他のプロパティの型も同じような方法でサブクラスにできる。

このアプローチはstructured dataでも動作する。日付の範囲を表すPythonのFuzzyDateクラスを持っていると仮定する。それはfistとlastのフィールドを持っていて、日付の範囲を開始と終了で格納している。

StructuredPropertyから派生しているFuzzyDateProperyを作成することが出来る。しかし残念ながら、後者は昔ながらのPythonクラスでは動作しない。Modelのサブクラスが必要である。中間モデルとしてModelのサブクラスを定義しよう。

次に、FuzzyDateとFuzzyDataModelを変換する為にFuzzyDateモデルのmodelclassの属性のコードと、_to_base_type()と_from_base_typeメソッドを定義したSturucturedProperyのサブクラスを定義する。

アプリケーションでは以下のように使用する。


FuzzyDateProperyにFuzzyDateオブジェクトのようにdateオブジェクトを格納したいと仮定すると、_validate()メソッドは以下のように変更する。

以下のようにFuzzyDateProperyクラスの代わりに使用する事も出来る。


MaybeFuzzyDateProperyフィールドを割り当てるときにMaybeFuzzyDateProperty._validate()とFuzzyDateProperty._validate()が両方呼び出される。同じ事が_to_base_type()と_from_base_type()に適用される。スーパークラスとサブクラス内のメソッドは暗黙的に結合される。

今回は自前でプロパティを用意したい場合の方法を紹介した。
次回はNDBのQueryについて。

0 件のコメント:

コメントを投稿