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 22

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

Posted at

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

go + ginを使いまわしてEchoでTodoアプリを作ってみました。

環境:Macbook + Goland

#機能概要

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

Echoのインストール

Echoの公式サイト

go get github.com/labstack/echo/v4

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

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>
<head>

</head>
<body>

<form role="form" action="/create" 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="/edit?id={{.ID}}">{{ .ID }}:{{ .Content }}</a></p> <a href="/destroy?id={{ .ID }}">[削除]</a>
{{end}}

</body>
</html>

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

<html>
<h1>

</h1>
<form role="form" action="/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>

処理関数を作成する

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

package main

import (
	"github.com/labstack/echo/v4"
	"go_echo_todo/models"
	"html/template"
	"io"
	"log"
	"net/http"
	"strconv"
)

func main() {

	e := echo.New()

	renderer := &TemplateRenderer{
		templates: template.Must(template.ParseGlob("templates/*.html")),
	}

	e.Renderer = renderer

	e.GET("/", func(c echo.Context) error {
		return c.String(http.StatusOK, "Hello, World!")
	})

	e.GET("/list", list)
	e.POST("/create", create)
	e.GET("/edit", edit)
	e.POST("/update", update)
	e.GET("/destroy", destroy)

	e.Logger.Fatal(e.Start(":1323"))
}

type TemplateRenderer struct {
	templates *template.Template
}

func (t *TemplateRenderer) Render(w io.Writer, name string, data interface{}, c echo.Context) error {

	// Add global methods if data is a map
	if viewContext, isMap := data.(map[string]interface{}); isMap {
		viewContext["reverse"] = c.Echo().Reverse
	}

	return t.templates.ExecuteTemplate(w, name, data)
}

// delete
func destroy(c echo.Context) error {
	id, err := strconv.Atoi(c.QueryParam("id"))
	if err != nil {
		log.Fatalln(err)
	}
	models.DeleteTodo(id)

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

// update
func update(c echo.Context) error {
	id, _ := strconv.Atoi(c.FormValue("id"))
	content := c.FormValue("content")
	todo, _ := models.GetTodo(id)
	todo.Content = content
	models.UpdateTodo(todo)

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

// edit
func edit(c echo.Context) error {
	id, err := strconv.Atoi(c.QueryParam("id"))
	if err != nil {
		log.Fatalln(err)
	}
	todo, _ := models.GetTodo(id)

	data := map[string]interface{}{
		"todo": todo,
	}

	return c.Render(http.StatusOK, "edit.html", data)
}

// create
func create(c echo.Context) error {
	content := c.FormValue("content")

	models.CreateTodo(content)

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

// list
func list(c echo.Context) error {

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

	data := map[string]interface{}{
		"todos": todos,
	}

	return c.Render(http.StatusOK, "list.html", data)
}

実行イメージ

go run server.go

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

以上になります。

ありがとうございました。

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?