GAE/GO CloudSQLを使ってみる

ルーティングとORMには以下のものを使わせてもらいます。
goは好きなものを選択して作れるのがよいですね。

gojiはシンプルで高速なWAFです。
middlewareを足すことで機能を追加することができます。

ORMは他にcoopernurse/gorpも有名らしいですが、gormのほうがドキュメントが充実しており機能も豊富なのでgormを使ってみます。

goapp get github.com/zenazn/goji
goapp get github.com/jinzhu/gorm

テーブル作成

cakephpのチュートリアルのサンプルを拝借しました。

1
2
3
4
5
6
7
8
CREATE TABLE `posts` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`title` varchar(50) DEFAULT NULL,
`body` text,
`created_at` datetime DEFAULT NULL,
`updated_at` datetime DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=12 DEFAULT CHARSET=utf8

createdとmodifiedの部分はgormに合わせて変更しました。
サンプルの内容も入れておきます。

違うテーブル名やカラム名でもマッピングできるようですが、
最初ならマイグレーションファイル作るのがよさそうです。
http://qiita.com/masahikoofjoyto/items/b2e6c2cad447e48f91ee

対応するmodel

1
2
3
4
5
6
7
type Post struct {
Id int
Title string
Body string
CreatedAt time.Time
UpdatedAt time.Time
}

ルーティング

簡単にルーティングを作成します。
gojiはgaeから使う場合はhttp.Handle("/", goji.DefaultMux)とします。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var db gorm.DB
func init() {
db, _ = gorm.Open("mysql", "root@cloudsql(projectId:CouldSQLInstanceId)/dbname?charset=utf8&parseTime=True&loc=Japan")
posts := web.New()
goji.Handle("/posts/*", posts)
posts.Use(middleware.SubRouter)
posts.Get("/", View)
posts.Get("/:id", ViewById)
posts.Post("/edit/", Update)
http.Handle("/", goji.DefaultMux)
}

datetimeのマッピング時に標準時が使用されるため接続文字列にloc=Japanが必要でした。
posts以下をまとめるにはSubRouterというmiddlewareを使用します。

一覧をjsonで出力

1
2
3
4
5
6
7
8
9
func View(c web.C, w http.ResponseWriter, r *http.Request) {
posts := []Post{}
//全件取得
db.Find(&posts)
w.Header().Set("Content-Type", "application/json; charset=utf-8")
encoder := json.NewEncoder(w)
encoder.Encode(&posts)
}

一件取得

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
func ViewById(c web.C, w http.ResponseWriter, r *http.Request) {
post := Post{}
id, err := strconv.Atoi(c.URLParams["id"])
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
//Id指定で取得
db.Find(&post, id)
w.Header().Set("Content-Type", "application/json; charset=utf-8")
encoder := json.NewEncoder(w)
encoder.Encode(&post)
}

jsonをpostして新規追加と編集

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
func Update(c web.C, w http.ResponseWriter, r *http.Request) {
post := Post{}
err := json.NewDecoder(r.Body).Decode(&post)
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
postForSave := Post{}
//Id指定して取得して上書き
db.First(&postForSave, post.Id)
postForSave.Title = post.Title
postForSave.Body = post.Body
db.Save(&postForSave)
}

そのままSaveできれば便利だったのですが、created_atが消えてしまいました。
ちょっといまいちな感じになっています。

{"Id":2,"Title":"タイトル編集","Body":"本文編集"}
Idありでpostすると編集され、

{"Title":"タイトル新規追加","Body":"本文新規追加"}
Idがないと新規追加されます。

テストやDB側が複雑になった場合の不安はありますが、
簡単なものであればすぐに作れそうです。