前回の「Go言語 ORMライブラリ GORMの使い方」に続いて「GORM+Gin」でTODOリストを作ってみました。使い方は「GitHubのREADME」を参考にしました。できたものは下記URLから確認できます。装飾は別途やれればと。
http://gin.taisablog.com/todo
事前情報
- Webフレームワーク:Gin (https://github.com/gin-gonic/gin)
- ORM:GORM (https://gorm.io/docs)
- DB:MySQL
ルーティングは通常のフォームだとPUT/DELETE
が使えないので以下のようにしました。
[GIN-debug] GET /todo --> main.main.func1 (3 handlers) // 一覧表示 [GIN-debug] POST /todo --> main.main.func2 (3 handlers) // 新規作成 [GIN-debug] GET /todo/:id --> main.main.func3 (3 handlers) // 編集画面表示 [GIN-debug] POST /todo/edit/:id --> main.main.func4 (3 handlers) // 編集 [GIN-debug] POST /todo/delete/:id --> main.main.func5 (3 handlers) // 削除
main.go
だけで作成した場合
main.go
に全ての処理を記述しています。
package main import ( "github.com/gin-gonic/gin" "github.com/jinzhu/gorm" _ "github.com/jinzhu/gorm/dialects/mysql" "net/http" "strconv" ) type Todo struct { gorm.Model Text string Status uint64 } func main() { db, err := gorm.Open("mysql", "gorm:gorm@/sandbox_gin?charset=utf8mb4&parseTime=True&loc=Local") if err != nil { panic("データベースへの接続に失敗しました") } defer db.Close() db.LogMode(true) db.AutoMigrate(&Todo{}) r := gin.Default() r.LoadHTMLGlob("templates/*") // 一覧画面 r.GET("/todo", func(c *gin.Context) { var todos []Todo db.Find(&todos) c.HTML(http.StatusOK, "index.html", gin.H{ "todos": todos, }) }) // 新規作成 r.POST("/todo", func(c *gin.Context) { text, _ := c.GetPostForm("text") status, _ := c.GetPostForm("status") istatus, _ := strconv.ParseUint(status, 10, 32) db.Create(&Todo{Text: text, Status: istatus}) c.Redirect(http.StatusMovedPermanently, "/todo") }) // 編集画面 r.GET("/todo/:id", func(c *gin.Context) { todo := Todo{} id := c.Param("id") db.First(&todo, id) c.HTML(http.StatusOK, "edit.html", gin.H{ "todo": todo, }) }) // 編集 r.POST("/todo/edit/:id", func(c *gin.Context) { todo := Todo{} id := c.Param("id") text, _ := c.GetPostForm("text") status, _ := c.GetPostForm("status") istatus, _ := strconv.ParseUint(status, 10, 32) db.First(&todo, id) todo.Text = text todo.Status = istatus db.Save(&todo) c.Redirect(http.StatusMovedPermanently, "/todo") }) // 削除 r.POST("/todo/delete/:id", func(c *gin.Context) { todo := Todo{} id := c.Param("id") db.First(&todo, id) db.Delete(&todo) c.Redirect(http.StatusMovedPermanently, "/todo") }) r.Run(":9000") }
ファイルを分割した場合
main.go
だけだとちょっと味気ないのでWebフレームワークっぽい構成にしてみました。書き方は色々だと思うので「参考」としてみてもらえればと思います。
ディレクトリ構成
. ├── controllers │ └── todo.go ├── db │ └── db.go ├── main.go ├── models │ └── todo.go ├── router │ └── router.go └── templates ├── edit.html └── index.html
main.go
DB
初期化とRouter
初期化の呼び出し
package main import ( "github.com/taisa831/sandbox-gin/db" "github.com/taisa831/sandbox-gin/router" ) func main() { dbConn := db.Init() router.Router(dbConn) }
db/db.go
DB
初期化
package db import ( "github.com/jinzhu/gorm" _ "github.com/jinzhu/gorm/dialects/mysql" "github.com/taisa831/sandbox-gin/models" ) func Init() *gorm.DB { db, err := gorm.Open("mysql", "gorm:gorm@/sandbox_gin?charset=utf8mb4&parseTime=True&loc=Local") if err != nil { panic("データベースへの接続に失敗しました") } db.LogMode(true) db.AutoMigrate(&models.Todo{}) return db }
models/todo.go
models
にはモデルの情報だけ記述
package models import "github.com/jinzhu/gorm" type Todo struct { gorm.Model Text string Status uint64 }
router/router.go
ルート情報を記述して起動
package router import ( "github.com/gin-gonic/gin" "github.com/jinzhu/gorm" "github.com/taisa831/sandbox-gin/controllers" ) func Router(dbConn *gorm.DB) { todoHandler := controllers.TodoHandler{ Db: dbConn, } r := gin.Default() r.LoadHTMLGlob("templates/*") r.GET("/todo", todoHandler.GetAll) // 一覧画面 r.POST("/todo", todoHandler.CreateTask) // 新規作成 r.GET("/todo/:id", todoHandler.EditTask) // 編集画面 r.POST("/todo/edit/:id", todoHandler.UpdateTask) // 更新 r.POST("/todo/delete/:id", todoHandler.DeleteTask) // 削除 r.Run(":9000") }
controllers/todo.go
実際にWebから呼び出された時の処理
package controllers import ( "github.com/gin-gonic/gin" "github.com/jinzhu/gorm" "github.com/taisa831/sandbox-gin/models" "net/http" "strconv" ) type TodoHandler struct { Db *gorm.DB } func (h *TodoHandler) GetAll(c *gin.Context) { var todos []models.Todo h.Db.Find(&todos) c.HTML(http.StatusOK, "index.html", gin.H{ "todos": todos, }) } func (h *TodoHandler) CreateTask(c *gin.Context) { text, _ := c.GetPostForm("text") status, _ := c.GetPostForm("status") istatus, _ := strconv.ParseUint(status, 10, 32) h.Db.Create(&models.Todo{Text: text, Status: istatus}) c.Redirect(http.StatusMovedPermanently, "/todo") } func (h *TodoHandler) EditTask(c *gin.Context) { todo := models.Todo{} id := c.Param("id") h.Db.First(&todo, id) c.HTML(http.StatusOK, "edit.html", gin.H{ "todo": todo, }) } func (h *TodoHandler) UpdateTask(c *gin.Context) { todo := models.Todo{} id := c.Param("id") text, _ := c.GetPostForm("text") status, _ := c.GetPostForm("status") istatus, _ := strconv.ParseUint(status, 10, 32) h.Db.First(&todo, id) todo.Text = text todo.Status = istatus h.Db.Save(&todo) c.Redirect(http.StatusMovedPermanently, "/todo") } func (h *TodoHandler) DeleteTask(c *gin.Context) { todo := models.Todo{} id := c.Param("id") h.Db.First(&todo, id) h.Db.Delete(&todo) c.Redirect(http.StatusMovedPermanently, "/todo") }
templates/index.html
TODOリスト 新規追加: 未対応 対応中 完了
TODO | ステータス | ||
{{.Text}} | {{if eq .Status 1}} 未対応 {{else if eq .Status 2}} 対応中 {{else if eq .Status 3}} 完了 {{end}} | 編集 | 削除 |
templates/edit.html
TODOリスト 編集 テキスト
未対応 対応中 完了
GitHubはこちら
https://github.com/taisa831/sandbox-gin