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]