前回の「Go言語 ORMライブラリ GORMの使い方」に続いて「GORM+Gin」でTODOリストを作ってみました。使い方は「GitHubのREADME」を参考にしました。
- 事前情報
- main.goだけで作成した場合
- ファイルを分割した場合
- ディレクトリ構成
- main.go
- db/db.go
- models/todo.go
- router/router.go
- controllers/todo.go
事前情報
- 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
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")
}