都営地下鉄の大門駅はJR山手線の浜松町駅のすぐ近くにあったり、羽田空港へのアクセスが良かったりと出張が多い人には大変便利な場所です。また、東京の景色を360度見渡すことが出来る世界貿易センタービルもちょっとした観光にはう […]
beego で実装した掲示板サービスの Go言語を読む〜その1〜
前回までマニュアルと雰囲気でなすがままに実装をしていて、フルスタックフレームワークでのコード自動生成という強力なサポートもありながら、Go 自体をあまり知らなくともできていました。
しかし細かい実装をするには自分の書いている言語、そして自動生成されたコード自体について最低限理解しておくべきというのは公然の事実と言えます。
ってことで自動生成したコードを中心に読んでいき、A Tour of Go のリンクなどを交えて解説していこうと思います。
[adinserter block="1"]
なお筆者は A Tour of Go を一週した程度なのでそんなに高度な解説は期待しないでください。しかし初心者なりに初心者の目線でわかりやすく説明を行うことを心掛けていこうと思います。
教材は前回までやってきた go言語での開発始めてみる?beego で掲示板っぽいもの作ってみる編 で作成した 掲示板風アプリ です。
/models/post.go
記念すべき読んでいく第一号はやはりローレイヤということで models の中かなと思い、/models/post.go を見ていこうと思います。
init 関数まで
https://github.com/haruhikonyan/beegotest/blob/master/haruch/models/post.go#L1-L20
まずはこの諸々宣言や go では特別な意味を持つ init()
までを確認していこうと思います。
-
package
package models
Goのプログラムは、パッケージ( package )で構成されます。
ということで、これは beego というフレームワークにおけるmodels
のファイル群であることを示しています。 -
import
import ( "errors" "fmt" "reflect" "strings" "github.com/astaxie/beego/orm" )
この
post.go
で使用する必要なパッケージを import しています。
一旦個々の import しているパッケージの説明は省略します。 -
type Post struct
type Post struct { Id int64 `orm:"auto"` Title string `orm:"size(128)"` Body string `orm:"type(longtext)"` }
struct とは構造体のことで、個人的には java などの class みたいなもだと思っています。C言語にもあるみたいですね。(筆者はCわからない)
Post
という struct を new すると、各 field に . つなぎでアクセスしたり、そこに値を代入できたりします。
Post というのは前回まで作っていた掲示板のDBへ保存する書き込みレコード1つ分に当たり、field として一意の Id と Title、Body を持ちます。
それぞれスペース区切りの二つ目が型を表しており、3つ目はorm
が内部的に使う tag です。
タグについては struct の機能で この辺 参照していただければと思います。
ちょっと orm の公式を探しても tag の意味がまとまってるページが見つかりませんでしたが orm のドキュメントにはちょいちょい意味が書いてあったりします。 -
func init()
func init() { orm.RegisterModel(new(Post)) }
init()
は特別な関数で、main()
よりも先に実行され、パッケージの初期化などに使われます。
ここでは import したgithub.com/astaxie/beego/orm
のRegisterModel()
を呼び出していて、orm で扱う Post モデルの初期化をしています。
[adinserter block="1"]
各種CRUD
https://github.com/haruhikonyan/beegotest/blob/master/haruch/models/post.go#L22-L143
続いて実際にCRUDのためのコードを読んでいきます。これも自動生成されたコード達です。
-
AddPost
// AddPost insert a new Post into database and returns // last inserted Id on success. func AddPost(m *Post) (id int64, err error) { o := orm.NewOrm() id, err = o.Insert(m) return }
公式ドキュメントのそのままで
orm
の機能を使い、DBに書き込むメソッドです。
任意のPost
を引数m
として取り、orm.NewOrm()
でorm
のインスタンスをo
という変数に取り、Insert()
へ引数として受け取ったPost
を渡してしてDBに保存しています。
Insert()
の返り値としては保存したレコードのid
とエラーがあればerr
返し、それをそのままAddPost
の返り値として return しています。
return としか書いていなくとも関数の宣言で(id int64, err error)
と引数に名前がついているため、その名前の変数が自動的に return されます。これを Go におけるnaked returnといいます。
また、Go の特徴の一つに返り値に複数を返せるというところがあります。 -
GetPostById
// GetPostById retrieves Post by Id. Returns error if // Id doesn't exist func GetPostById(id int64) (v *Post, err error) { o := orm.NewOrm() v = &Post{Id: id} if err = o.QueryTable(new(Post)).Filter("Id", id).RelatedSel().One(v); err == nil { return v, nil } return nil, err }
こちらは
id
を受け取って Post モデルの実体を DB から取得して返すメソッドです。
o
は先ほどと同じくorm
のインスタンスを生成しており、v = &Post{Id: id}
にて上部で定義したPost
struct を新たに定義し、そのフィールドの Id に 引数で受け取ったid
をセットしてして、変数v
へ &がついているのでアドレスを代入しています。
struct のフィールドや定義についてはこの辺りを参照ください。
また、ポインタについてはこの辺りを参照ください。
o.QueryTable(new(Post)).Filter("Id", id).RelatedSel().One(v)
この処理ですが順に、- o.QueryTable(new(Post))
まずorm
のQueryTable()メソッドで一番上の例の2つ目のオブジェクト自体をテーブル名として使い、QuerySeter
オブジェクトを取得します。(QuerySeterってなんだ?)
QuerySeter は任意の table にクエリを実行するためのものという認識です。 - .Filter("Id", id).RelatedSel().One(v)
Filter() でId
を指定することで SQL でいうWHERE Id = 1
で絞り込みを行い、RelatedSel() で、何も引数を指定しないことで、関連テーブルをすべて Join します。最後に One(v) で、v
つまり Post レコードを一つ取得します。 - if err = 式; err == nil {}
if文の書き方ですね。err
を取得し、もしerr
が nil であれば、エラーが無いということなので、取得した Postv
とerr
を nil で返し、もしerr
が存在したらエラーを retrun するという構造になっています。
- o.QueryTable(new(Post))
[adinserter block="1"]
まとめ
今回は一旦ここまでにします。
最初にも言った通り筆者は A Tour of Go を軽く一週した程度だけなのですが、フレームワークのドキュメントを見つつ、Goの静的型付け言語という特性でエディタの保管をうまく活用していけばそこまで身構える必要も無いという印象でした。
次回も引き続き /models/post.go
を読んでいこうと思います。 GetAllPost()
では interface も出てくるので今一度読み直そうかなと思っているところでもあります。
>Go言語での開発を試してみる 〜調べる編〜
>go言語での開発始めてみる〜開発環境を作る編〜
>Go言語での開発始めてみる〜beego のコード自動作成機能を試す編〜
haruhikonyan
大きな野望を抱くホルン吹き。