GAE本。
Google API Expertが解説する Google App Engine for Java実践ガイド
- 作者: 小川信一
- 出版社/メーカー: インプレス
- 発売日: 2012/03/16
- メディア: 大型本
- クリック: 12回
- この商品を含むブログ (14件) を見る
台風が、自分の出身地である岡山を珍しく直撃しているこの連休、その岡山に私用で帰省しているんだけど、先週も参加させて頂いた #golangcafe が今週も開催されるということで(本来ならば日曜夜なんだけど、無理言って今日にしていただいたm( )m)、2週連続で参加させて頂いた。
今回はもくもく形式で、ということだったので、ちょうど先週から取り組んでいる GAE本の Go での写経の続き・3.2章をやることに。#golangcafe は2時間ほどで切り上げる形となったんだけど、家に戻ってからも少し手を付けて、なんとかこの章は終えられたので、この章の写経を通じて学んだことをメモしておくことにする。
3.2章の概要
- 簡単な議事録Webアプリケーションの作成。
- 「議事録」(Minutes)エンティティの登録と、各議事録に登録できる「メモ」(Memo)エンティティの登録が主な機能
- デプロイ済みのアプリケーション
- 3.2章の写経のプルリクエスト
スクショは↓なかんじ。
学んだことメモ
Datastore に記録される struct には、デフォルトでは そのエンティティの key は含まれない
例えば下記のような struct を使って Datastore に登録したのちにそのエンティティを get したとき、そのエンティティの key の情報は含まれない。
type Minutes struct { Title string CreatedAt time.Time }
何らかの理由で key の情報が必要な場合は、そのためのエンティティを struct 内に設けてやる必要がある。ということで、単純に考えると下記のようになると思う。
type Minutes struct { Key *datastore.Key Title string CreatedAt time.Time }
key := datastore.NewIncompleteKey(c, "minutes", nil) m1 := Minutes{ Key: key, Title: title, CreatedAt: time.Now(), } // put _, err := datastore.Put(c, key, &m1)
ただこの実装だと、Datastore に登録されるエンティティのプロパティに Incomplete な key を登録してしまう形となり、BadValueError: Incomplete key found for reference property Key.
エラーが出て admin console から datastore の中身を覗けなくなってしまう(これの解消については後述)。
これを避けるためには、エンティティの登録前に完全な key を作成するか、「エンティティを get したときに key を struct にもたせてやる」というようなアプローチが必要になる。
下記のツイートを起点としたやりとりが、とても参考になった。公式クライアントとかで見てみて欲しい。
GAEのdatastoreで、entityのkeyをレスポンスjsonに含ませたいのだけど、なんかキツそうだったらentityにkeyと同じ値のpropertyを持たせたほうがいいのかなぁ、というのを漠然と考えている
— PSN: k-imanaka (@pside) 2014年9月25日
これの理想的なアプローチとしてはやはり、「エンティティ取得時に key を持たせてくれるような仕組みを作る」ということなんだろうけど(それくらいしか思いつかない)、とはいえ今回作ってるのはガチのアプリケーションではなく、サンプルアプリの写経なので(言い訳)、UUID を key_name にして完全な key を作って対応することにした。泣きながら。
$ go get code.google.com/p/go-uuid
- Mercurial がないと
go get
できないので入れる...(´・ω・`) $ brew install mercurial
- Mercurial がないと
- 気を取り直して、
$ go get code.google.com/p/go-uuid
最終的な実装は、下記のような形で落ち着いた。
key := datastore.NewKey(c, "memo", uuid.New(), 0, nil)
dev server の Datastore の中身をクリアしたいときは
ローカルでの動作確認時に Datastore に put したデータは、ローカルでファイルとして管理されており、内容をクリアすることもできる。
上で後述、と書いたケースのような、incomplete な key をプロパティに持つエンティティを登録してしまった、などといったときに Datastore をまるごとクリアしたいときは、 dev_appserver.py --clear_datastore=yes myapp
で、dev server の datastore をクリアしてからの dev server の立ち上げができる。
The Go Development Server | App Engine standard environment for Go | Google Cloud も参考に。
リクエストパラメータの取得方法について
クライアント(今回は html(JS))からのリクエストパラメータは Request#FormValue
で取れる。POST / GET 両方とも、これで取れる :)
func Post(w http.ResponseWriter, r *http.Request) { title := r.FormValue("title")
おまけ。取った値の validation を if value == nil
とかでやろうとしてしまったけど、string のゼロ値は空文字 ""
だった...。これ、しばらく何度か同じミスしそう。
HTTP メソッドでの切り分けはいい方法あるかな?
お手本では GET/POST のメソッドで処理の切り分けができるようにしてるけど、 gae/g でこれをスマートにやるとしたらどんな方法がいいのかな。とりあえず今回はそこは意図的に無視した。
プロパティに Key
を持つエンティティに対するクエリフィルタって...
どうやるのかなと思って、単純に下記のように書いてみた。
あと、降順のときはそのプロパティ名称の前に -
を付けてたので、その逆だから +
かな、と思ったんだけど。
q := datastore.NewQuery("memo").Filter("Minutes", minutesKey).Order("+CreatedAt")
結論、これだとダメで、下記のようにするのが正解。Filter のときに等号がいるのと、昇順の場合は記号は不要だった。ドキュメント見てねって話。
q := datastore.NewQuery("memo").Filter("Minutes =", minutesKey).Order("CreatedAt")
そのプロパティが key であることを気にする必要はないような書き方だけど、フィルタの対象が「そのエンティティのキー」の場合だと、下記のように書く必要があるみたい。
q := datastore.NewQuery("Person").Filter("__key__ >", lastSeenKey)
(これって、key での get になるのかな。それとも query?)
slim3 でいう Datastore#stringToKey(keyToString)は...
Key#Encode()
Key#DecodeKey(string)
で可能。
minutesKeyString := r.FormValue("minutes")
minutesKey, err := datastore.DecodeKey(minutesKeyString)
しかも、Key
プロパティを持つエンティティを Json#Marshal
してクライアントに返す場合でも...
@a_know datastore.KeyはMarshalJSON、UnmarshalJSONを実装してるのでよきにはからってくれます^^
— Daigo Ikeda (@hogedigo) 2014年10月13日
素敵。抱いて!!
次は 3.3章、Users Service。
今度も同じような感じで写経&わかったことをメモしようと思う。