0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

ひとりでやれるかな?Advent Calendar 2022

Day 14

go + gin + gormでtodo Webアプリを作ってみた

Last updated at Posted at 2022-12-16

go + gin + gormでtodo Webアプリを最速で作ってみた

go歴3ヶ月でtodo webアプリを作ってみました。

環境:Macbook + Goland

#機能概要

  • todoリスト
  • todo作成
  • todo更新
  • todo削除

ginのインストール

ginの公式サイト

go get -u github.com/gin-gonic/gin

その他ライブラリのインストール

GORM:O/Rマッパー

gormの公式サイト

  • gormのインストール
    go get -u gorm.io/gorm
  • gorm sqliteドライバのインストール
    go get -u gorm.io/driver/sqlite

configライブラリのインストール

go get gopkg.in/ini.v1

todoアプリの開発

ディレクトリ構成

・config
コンフィグ設定ファイル
・models
モデルファイル
・templates
テンプレートディレクトリ

configファイルを作成する

config.iniを編集する

[web]
logfile = webapp.log
[db]
driver = sqlite3
name = webapp.sql

コンフィグファイルをロードする

config.iniを変数にロードする
config/config.go

package config

import (
	"database/sql"
	"gopkg.in/ini.v1"
	"log"
)

type ConfigList struct {
	SQLDriver string
	DbName    string
	LogFile   string
}

var Db *sql.DB
var err error
var Config ConfigList

func init() {
	LoadConfig()
}

func LoadConfig() {
	cfg, err := ini.Load("config.ini")
	if err != nil {
		log.Fatalln(err)
	}
	Config = ConfigList{
		SQLDriver: cfg.Section("db").Key("driver").String(),
		DbName:    cfg.Section("db").Key("name").String(),
		LogFile:   cfg.Section("web").Key("logfile").String(),
	}
}

モデルを作成する

ファイル:base.go
初期テーブルの作成やDBインスタンスの作成

package models

import (
	"fmt"
	_ "github.com/mattn/go-sqlite3"
	"go_gin_todo/config"
	"gorm.io/driver/sqlite"
	"gorm.io/gorm"
)

var Db *gorm.DB

var err error

const (
	tableNameTodo = "todos"
)

func init() {

	Db, err = gorm.Open(sqlite.Open(config.Config.DbName))

	if err != nil {
		fmt.Errorf("error:%v", err)
	}
	Db.AutoMigrate(&Todo{})

}

ファイル:todo.go
todoテーブルのO/RマッピングモデルとCRUD関数の定義を行う

package models

import (
	"gorm.io/gorm"
)

type Todo struct {
	gorm.Model
	Content string
}

func CreateTodo(content string) (err error) {

	todo := Todo{
		Content: content,
	}
	Db.Create(&todo)

	return err
}

func DeleteTodo(id int) (err error) {

	Db.Delete(&Todo{}, id)

	return err
}

func GetTodo(id int) (todo Todo, err error) {

	Db.Find(&todo, id)

	return todo, err
}

func UpdateTodo(t Todo) (err error) {

	Db.Save(&t)

	return err
}

テンプレートファイルを作成する

ファイル:templates/todo/list.html
todoリストと作成フォームのhtml

<html>
    <h1>
    {{ .title }}
    </h1>

    <form role="form" action="/todos/save" method="POST">
      <div class="lead">Todos作成</div>
      <div class="form-group">
        <textarea class="form-control" name="content" id="content" placeholder="Todoを追加" rows="4"></textarea>
        <br/>
        <br/>
        <button class="btn btn-lg btn-primary pull-right" type="submit">作成</button>
      </div>
    </form>

    <hr />
    {{ range .todos }}
        <p><a href="/todos/edit?id={{.ID}}">{{ .ID }}:{{ .Content }}</a></p> <a href="/todos/destroy?id={{ .ID }}">[削除]</a>
    {{end}}
</html>

ファイル:templates/edit.html
todo変更フォーム

<html>
    <h1>
    {{ .title }}
    </h1>
    <form role="form" action="/todos/update" method="POST">
    <input type="hidden" name="id" value="{{.todo.ID}}" />
      <div class="lead">Todos更新</div>
      <div class="form-group">
        <textarea class="form-control" name="content" id="content" placeholder="Todoを追加" rows="4">{{.todo.Content}}</textarea>
        <br/>
        <br/>
        <button class="btn btn-lg btn-primary pull-right" type="submit">更新</button>
      </div>
    </form>
    <hr />
</html>

処理関数を作成する

ファイル:main.go
todoのCRUD処理を行うメインファイル

package main

import (
	"github.com/gin-gonic/gin"
	"go_gin_todo/config"
	"go_gin_todo/models"
	"io"
	"log"
	"net/http"
	"os"
	"strconv"
)

func setupRouter() *gin.Engine {

	//ログ設定
	f, _ := os.Create(config.Config.LogFile)
	gin.DefaultWriter = io.MultiWriter(os.Stdout, f)

	//templateディレクトリ設定
	r := gin.Default()
	r.LoadHTMLGlob("templates/**/*")

	// todo create
	r.POST("/todos/save", func(c *gin.Context) {

		models.CreateTodo(c.PostForm("content"))

		c.Redirect(http.StatusMovedPermanently, "/todos/list")
	})

	// todo create
	r.POST("/todos/update", func(c *gin.Context) {
		id, _ := strconv.Atoi(c.PostForm("id"))
		content := c.PostForm("content")
		todo, _ := models.GetTodo(id)
		todo.Content = content
		models.UpdateTodo(todo)

		c.Redirect(http.StatusMovedPermanently, "/todos/list")
	})

	//todo edit
	r.GET("/todos/edit", func(c *gin.Context) {
		id, err := strconv.Atoi(c.Query("id"))
		if err != nil {
			log.Fatalln(err)
		}
		todo, _ := models.GetTodo(id)

		c.HTML(http.StatusOK, "edit.tmpl", gin.H{
			"title": "Todo",
			"todo":  todo,
		})
	})

	//todo delete
	r.GET("/todos/destroy", func(c *gin.Context) {
		id, err := strconv.Atoi(c.Query("id"))
		if err != nil {
			log.Fatalln(err)
		}
		models.DeleteTodo(id)

		c.Redirect(http.StatusMovedPermanently, "/todos/list")
	})
    //todo list
	r.GET("/todos/list", func(c *gin.Context) {

		var todos []models.Todo
		models.Db.Find(&todos)

		c.HTML(http.StatusOK, "list.tmpl", gin.H{
			"title": "Todo",
			"todos": todos,
		})
	})

	return r
}

func main() {

	r := setupRouter()
	r.Run(":8080")
}

実行イメージ

go run main.go

ブラウザで以下を開く
http://localhost:8080/todos/list

以上になります。

webアプリを作成するにはかなりお手軽なフレームワークかと思います。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?