Go言語 GORM+GinでTODOリストを作ってみた


前回の「Go言語 ORMライブラリ GORMの使い方」に続いて「GORM+Gin」でTODOリストを作ってみました。使い方は「GitHubのREADME」を参考にしました。できたものは下記URLから確認できます。装飾は別途やれればと。
http://gin.taisablog.com/todo

事前情報

ルーティングは通常のフォームだと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リスト
新規追加:
未対応
対応中
完了

{{range .todos}} {{end}}
TODO ステータス
{{.Text}} {{if eq .Status 1}} 未対応 {{else if eq .Status 2}} 対応中 {{else if eq .Status 3}} 完了 {{end}} 編集 削除
$(function(){ $('.delete').on('click', function(){ $('#frmDelete').attr('action', '/todo/delete/' + $(this).data('id')) $('#frmDelete').submit() }) })

templates/edit.html

TODOリスト 編集
テキスト
未対応 対応中 完了

GitHubはこちら
https://github.com/taisa831/sandbox-gin


カテゴリー: Go

関連記事