Go for it!

はてブロドメインで仮運用中。

GAEのモデルにプロパティを追加する

GAE HealthMeterでダイレクトメッセージに対応した関係で、モデルに表示・非表示のプロパティを追加した。SDK上で開発している分には良かったのだが、App Engine上にデプロイしたところ問題が発覚したのでメモ。

[ad#text_only_square]

HealthMeterにはTwitモデルという、ユーザからのリプライを保存するモデルが存在します。Twitterから取得可能ないくつかの値を保持するよう、以下のように定義されています。

[code lang=“python”] class Twit(db.Model): ‘’‘copy of twitter status’‘’ uid=db.IntegerProperty() screen_name=db.StringProperty() status=db.StringProperty() status_id=db.IntegerProperty() secret=db.BooleanProperty(default=False) posted_at = db.DateTimeProperty(auto_now_add=True) created_at = db.DateTimeProperty(auto_now_add=True) [/code]

今回、secretプロパティを追加したのですが、App Engine上にデプロイしたところ以下のことが解りました。

  • secret属性が有る時代のレコードにはTRUE/FALSEのいずれかが登録される
  • secret属性が無い時代のレコードのsecretプロパティはmissingになる

secretプロパティはデフォルトFALSEなので古いレコードはFALSEになっても良さそうなものですが、実際にはそう都合の良いことにはなりませんでした。

GQLにはSQLでいうNULL値のような扱いがないため、モデル内にプロパティが存在すれば、プロパティの値も存在すると解釈されます(たぶん)。しかし、モデルを途中で変更した場合、一部のレコードはプロパティが存在しない(有無でもnullでもなく、無い)ため、GQLで検索することが出来ません。

例えば次のようなGQL Queryではsecretプロパティがmissingとなっているレコードは検索できません。

[code lang=“sql”] SELECT * FROM Twit WHERE secret != TRUE SELECT * FROM Twit WHERE secret != FALSE [/code]

しかし、モデルに抽出条件を与えても、条件と関係なくレコードを取得することが可能です。この点だけはRDBMSのNULL値みたいな扱い。

[code lang=“python”]

取得可能

Twit.all().filter(‘secret =’, False).fetch(100)

これも取得可能

Twit.all().filter(‘secret =’, True).fetch(100) [/code]

これを利用するとプロパティを更新出来そうです。

プロパティがmissingになったレコードは、オブジェクトから参照するとFalseです。なので、次のようなコードを書いてモデル内のmissingを明示的に更新します。

[code lang=‘python’] class FixSecretHandler(webapp.RequestHandler): def get(self): for t in Twit.all().order(“-posted_at”): if t.secret==False: t.secret=False t.put() [/code]

障害が出たときはかなり焦りましたが、なんとかmissingとなったプロパティに正しい値を入れることが出来ました。

[ad#text_wide]