从零开始使用Gin+Gorm实现一个网页版的备忘小清单(二) | 青训营

64 阅读5分钟

连接数据库

这里我们使用的是MySQL数据库,如果不会安装MySQL或者懒得安装MySQL,可以使用一下命令快速运行一个MySQL8.0.19实例,当然前提是你要有docker环境…

在本地的13306端口运行一个名为mysql8019,root用户名密码为root1234的MySQL容器环境:

docker run --name mysql8019 -p 13306:3306 -e MYSQL_ROOT_PASSWORD=root1234 -d mysql:8.0.19

在另外启动一个MySQL Client连接上面的MySQL环境,密码为上一步指定的密码root1234:

docker run -it --network host --rm mysql mysql -h 127.0.0.1 -P 13306 --default-character-set=utf8mb4 -uroot -p

在使用GORM前手动创建数据库mydb

create database mydb;

首先引入一下GORMMySQL的包。

_ "github.com/jinzhu/gorm/dialects/mysql"

前面加下划线是因为只使用了其中的一部分,不加的话会报错。

定义一个全局的DB变量,方便后续的使用。

var (
    DB *gorm.DB
)

初始化数据库方法:

func initMySQL() (err error) {
    dsn := "root:root1234@(127.0.0.1:13306)/mydb?charset=utf8mb4&parseTime=True&loc=Local"
    DB, err = gorm.Open("mysql", dsn)
    if err != nil {
       return
    }
    return DB.DB().Ping()
}

main函数中调用initMySQL方法。

err := initMySQL()
if err != nil {
    panic(err)
}
defer DB.Close()

连接好数据库之后要进行模型的绑定,也就是让代码中的结构体(模型)能够对应数据库中的表,绑定完成后,GORM会自动在数据库创建一个名叫'todos'的表(自动给我们的结构体名加了复数),这个操作使用下面这行代码进行实现。

//模型绑定
DB.AutoMigrate(&Todo{})

完整代码:

package main

import (
    "github.com/gin-gonic/gin"
    "github.com/jinzhu/gorm"
    _ "github.com/jinzhu/gorm/dialects/mysql"
    "net/http"
)

var (
    DB *gorm.DB
)

// Todo model
type Todo struct {
    ID     int    `json:"id"`
    Title  string `json:"title"`
    Status bool   `json:"status"`
}

func initMySQL() (err error) {
    dsn := "root:root1234@(127.0.0.1:13306)/mydb?charset=utf8mb4&parseTime=True&loc=Local"
    DB, err = gorm.Open("mysql", dsn)
    if err != nil {
       return
    }
    return DB.DB().Ping()
}

func main() {
    //创建数据库
    //SQL: create database mydb;
    //连接数据库
    err := initMySQL()
    if err != nil {
       panic(err)
    }
    defer DB.Close()

    //模型绑定
    DB.AutoMigrate(&Todo{})

    r := gin.Default()
    
    //告诉gin框架模板文件引用的静态文件去哪里找
    r.Static("/static", "static")


    //告诉Gin框架去哪找模板文件
    r.LoadHTMLGlob("templates/*") //其中/*表示templates文件夹下的所有文件

    r.GET("/", func(c *gin.Context) {
       c.HTML(http.StatusOK, "index.html", nil)
    })

    //v1
    v1Group := r.Group("v1")
    {
       //待办事项
       //添加
       v1Group.POST("/todo", func(c *gin.Context) {

       })
       //查看
       v1Group.GET("/todo", func(c *gin.Context) {

       })
       //修改
       v1Group.PUT("/todo/:id", func(c *gin.Context) {

       })
       //删除
       v1Group.DELETE("/todo/:id", func(c *gin.Context) {

       })
    }

    r.Run()
}

ok,到这里我们就完成了数据库的连接和模型的绑定,现在我们可以开始对功能进行实现了。

功能实现

添加

v1Group.POST("/todo", func(c *gin.Context) {
    //前端页面填写待办事项,点击提交,会发请求到这里
    //1、从请求中把数据拿出来
    var todo Todo
    c.BindJSON(&todo)
    //2、存入数据库
    //3、返回响应
    if err = DB.Create(&todo).Error;err != nil {
       c.JSON(http.StatusOK,gin.H{"error" : err.Error()})
       return
    }else {
       c.JSON(http.StatusOK,todo)
    }
})

查看

v1Group.GET("/todo", func(c *gin.Context) {
    //查询todo这个表里的所有数据
    var todoList []Todo
    if err = DB.Find(&todoList).Error;err != nil {
       c.JSON(http.StatusOK, gin.H{"error": err.Error()})
       return
    }else {
       c.JSON(http.StatusOK,todoList)
    }
})

修改

v1Group.PUT("/todo/:id", func(c *gin.Context) {
    //获取路径参数
    id, ok := c.Params.Get("id")
    if !ok {
       c.JSON(http.StatusOK, gin.H{"error": "无效id"})
       return
    }
    var todo Todo
    if err = DB.Where("id=?", id).First(&todo).Error; err != nil {
       c.JSON(http.StatusOK, gin.H{"error": err.Error()})
       return
    }
    c.BindJSON(&todo)
    if err = DB.Save(&todo).Error; err != nil {
       c.JSON(http.StatusOK, gin.H{"error": err.Error()})
    } else {
       c.JSON(http.StatusOK, todo)
    }
})

删除

v1Group.DELETE("/todo/:id", func(c *gin.Context) {
    //获取路径参数
    id, ok := c.Params.Get("id")
    if !ok {
       c.JSON(http.StatusOK, gin.H{"error": "无效id"})
       return
    }
    if err = DB.Where("id=?", id).Delete(Todo{}).Error; err != nil {
       c.JSON(http.StatusOK, gin.H{"error": err.Error()})
       return
    } else {
       c.JSON(http.StatusOK, gin.H{id: "删除成功"})
    }
})

项目完整代码:

package main

import (
    "github.com/gin-gonic/gin"
    "github.com/jinzhu/gorm"
    _ "github.com/jinzhu/gorm/dialects/mysql"
    "net/http"
)

var (
    DB *gorm.DB
)

// Todo model
type Todo struct {
    ID     int    `json:"id"`
    Title  string `json:"title"`
    Status bool   `json:"status"`
}

func initMySQL() (err error) {
    dsn := "root:root1234@(127.0.0.1:13306)/mydb?charset=utf8mb4&parseTime=True&loc=Local"
    DB, err = gorm.Open("mysql", dsn)
    if err != nil {
       return
    }
    return DB.DB().Ping()
}

func main() {
    //创建数据库
    //SQL: create database mydb;
    //连接数据库
    err := initMySQL()
    if err != nil {
       panic(err)
    }
    defer DB.Close()

    //模型绑定
    DB.AutoMigrate(&Todo{})

    r := gin.Default()

    //告诉gin框架模板文件引用的静态文件去哪里找
    r.Static("/static", "static")

    //告诉Gin框架去哪找模板文件
    r.LoadHTMLGlob("templates/*") //其中/*表示templates文件夹下的所有文件

    r.GET("/", func(c *gin.Context) {
       c.HTML(http.StatusOK, "index.html", nil)
    })

    //v1
    v1Group := r.Group("v1")
    {
       //待办事项
       //添加
       v1Group.POST("/todo", func(c *gin.Context) {
          //前端页面填写待办事项,点击提交,会发请求到这里
          //1、从请求中把数据拿出来
          var todo Todo
          c.BindJSON(&todo)
          //2、存入数据库
          //3、返回响应
          if err = DB.Create(&todo).Error; err != nil {
             c.JSON(http.StatusOK, gin.H{"error": err.Error()})
             return
          } else {
             c.JSON(http.StatusOK, todo)
          }
       })
       //查看
       v1Group.GET("/todo", func(c *gin.Context) {
          //查询todo这个表里的所有数据
          var todoList []Todo
          if err = DB.Find(&todoList).Error; err != nil {
             c.JSON(http.StatusOK, gin.H{"error": err.Error()})
             return
          } else {
             c.JSON(http.StatusOK, todoList)
          }
       })
       //修改
       v1Group.PUT("/todo/:id", func(c *gin.Context) {
          //获取路径参数
          id, ok := c.Params.Get("id")
          if !ok {
             c.JSON(http.StatusOK, gin.H{"error": "无效id"})
             return
          }
          var todo Todo
          if err = DB.Where("id=?", id).First(&todo).Error; err != nil {
             c.JSON(http.StatusOK, gin.H{"error": err.Error()})
             return
          }
          c.BindJSON(&todo)
          if err = DB.Save(&todo).Error; err != nil {
             c.JSON(http.StatusOK, gin.H{"error": err.Error()})
          } else {
             c.JSON(http.StatusOK, todo)
          }
       })
       //删除
       v1Group.DELETE("/todo/:id", func(c *gin.Context) {
          //获取路径参数
          id, ok := c.Params.Get("id")
          if !ok {
             c.JSON(http.StatusOK, gin.H{"error": "无效id"})
             return
          }
          if err = DB.Where("id=?", id).Delete(Todo{}).Error; err != nil {
             c.JSON(http.StatusOK, gin.H{"error": err.Error()})
             return
          } else {
             c.JSON(http.StatusOK, gin.H{id: "删除成功"})
          }
       })
    }

    r.Run()
}

ok,到这里我们就实现了全部的功能我们现在来进行一下测试。

还是在Terminal中输入go run main.go。成功启动之后会显示程序正在监听8080端口。

21.jpg

打开浏览器,输入127.0.0.1:8080/

22.jpg

添加测试

在文本框中输入待办事项之后点击右侧的加号。

23.jpg

提示添加成功并且下方显示添加的待办事项。

24.jpg

修改测试

点击下方待办事项右侧的绿色按钮,可以将该待办事项的状态进行更改,表示该事项已完成。

25.jpg

删除测试

点击下方待办事项右侧的红色按钮,可以将待办事项进行删除。删除成功会进行提示并将下方列表中的待办事项删除。

26.jpg

ok,到这里我们就完成了对这个小项目所有功能的实现。

当然,这里还存在一个问题,那就是我们在这个项目中把所有的代码都写到了main.go一个文件中,这一点对于我们这样一个代码量较小的项目来说是无关紧要的,但是如果是一个需要团队协作的大项目,那么这样做就会不利于开发和测试了,这时候我们需要对其进行项目拆分,将不同的逻辑拆分到不同的目录的不同文件中,具体的拆分方式会因为项目的不同或者个人的偏好不同而不同,所以这里不做赘述,只要是适合的就是最好的。