gorm project practice
ER图
Relationship sorting
-
One-to-one relationship:
- User and UserLog: A user corresponds to a user log, through the primary key of the
User
model and theUserLog
model Foreign keys establish one-to-one relationships.
- User and UserLog: A user corresponds to a user log, through the primary key of the
-
One-to-many relationship:
- User and Teacher: A user can correspond to multiple teachers through the foreign key of the
Teacher
model (UserID
) to establish a one-to-many relationship with the primary key of theUser
model. - User and Student: A user can correspond to multiple students through the foreign key of the
Student
model (UserID
) to establish a one-to-many relationship with the primary key of theUser
model. - Teacher and Class: A teacher can have multiple classes through the foreign key of the
Class
model (TeacherID
) to establish a one-to-many relationship with the primary key of theTeacher
model. - Student and StudentClass: A student can have multiple classes through the
StudentClass
foreign key of the model (StudentID
) to establish a one-to-many relationship with the primary key of theStudent
model. - Student and Attendance: A student can have multiple attendance records through the foreign key of the
Attendance
model (StudentID
) Establish a one-to-many relationship with the primary key of theStudent
model.
- User and Teacher: A user can correspond to multiple teachers through the foreign key of the
-
Many-to-many relationship:
- Student and Class: A student can belong to multiple classes, and a class can have multiple students. The
StudentClass
model is used as an intermediate table to create Many-to-many relationship. - Teacher and Class: A teacher can teach multiple classes, and a class can have multiple teachers, through the foreign key of the
Class
model (< /span> the primary key of the model to establish a many-to-many relationship.TeacherID
) andTeacher
- Student and Class: A student can belong to multiple classes, and a class can have multiple students. The
-
One-to-many reverse relationship:
- Parent and Student: A parent can have multiple children through the foreign key of the
Student
model (StudentID
) to establish a one-to-many reverse relationship with the primary key of theParent
model.
- Parent and Student: A parent can have multiple children through the foreign key of the
Use gen to automatically generate code
First use the gen tool to generate code
package main
// gorm gen configure
import (
"fmt"
"gorm.io/driver/mysql"
"gorm.io/gorm"
"gorm.io/gen"
)
const MySQLDSN = "root:root@tcp(127.0.0.1:3306)/school?charset=utf8mb4&parseTime=True"
func connectDB(dsn string) *gorm.DB {
db, err := gorm.Open(mysql.Open(dsn))
if err != nil {
panic(fmt.Errorf("connect db fail: %w", err))
}
return db
}
func main() {
// 指定生成代码的具体相对目录(相对当前文件),默认为:./query
// 默认生成需要使用WithContext之后才可以查询的代码,但可以通过设置gen.WithoutContext禁用该模式
g := gen.NewGenerator(gen.Config{
// 默认会在 OutPath 目录生成CRUD代码,并且同目录下生成 model 包
// 所以OutPath最终package不能设置为model,在有数据库表同步的情况下会产生冲突
// 若一定要使用可以通过ModelPkgPath单独指定model package的名称
OutPath: "dao/query",
/* ModelPkgPath: "dal/model"*/
// gen.WithoutContext:禁用WithContext模式
// gen.WithDefaultQuery:生成一个全局Query对象Q
// gen.WithQueryInterface:生成Query接口
Mode: gen.WithDefaultQuery | gen.WithQueryInterface,
})
// 通常复用项目中已有的SQL连接配置db(*gorm.DB)
// 非必需,但如果需要复用连接时的gorm.Config或需要连接数据库同步表信息则必须设置
g.UseDB(connectDB(MySQLDSN))
// 从连接的数据库为所有表生成Model结构体和CRUD代码
// 也可以手动指定需要生成代码的数据表
g.ApplyBasic(g.GenerateAllTable()...)
// 执行并生成代码
g.Execute()
}
Define foreign key relationships in the model generated by gen
The generated model code is in dao/model
We need to define foreign key relationships in these Models. First define the relationships between the User table, Teacher table and Student table.
one-to-one relationship
First, determine the main table and supplementary table. The main table
-
Main table: User, gradually: UserID
-
Attached table: Student, foreign key: UserID
-
Attached table: Teacher, foreign key: UserID
-
Add two in model.User
-
Change generate, we need to use the changed model to generate query
-
g.ApplyBasic( model.Student{}, model.Teacher{}, model.User{}, model.UserLog{}, model.Class{}, model.Course{}, model.Attendance{}, model.StudentClass{}, model.Parent{}, ) // 执行并生成代码 g.Execute()
Add user business logic
Here press UserType to create corresponding students and teachers.
func CreateUser(c *gin.Context) {
var req request.CreateUserRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"code": http.StatusBadRequest, "error": "创建用户请求失败,无效的请求参数"})
return
}
var user model.User
switch req.UserType {
case "Student":
user = model.User{
Username: req.Username,
Password: req.Password,
OpenID: req.OpenID,
Avatar: req.Avatar,
LastLogin: time.Now(),
UserType: req.UserType,
IsValid: req.IsValid,
Student: model.Student{
StudentName: req.Username,
},
}
case "Teacher":
user = model.User{
Username: req.Username,
Password: req.Password,
OpenID: req.OpenID,
Avatar: req.Avatar,
LastLogin: time.Now(),
UserType: req.UserType,
IsValid: req.IsValid,
Teacher: model.Teacher{
TeacherName: req.Username,
},
}
default:
c.JSON(http.StatusBadRequest, gin.H{"code": http.StatusBadRequest, "error": "无效的用户类型"})
return
}
err := query.User.WithContext(context.Background()).Create(&user)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"code": http.StatusInternalServerError, "error": fmt.Sprintf("创建用户请求失败,无法创建用户: %v", err)})
return
}
c.JSON(http.StatusOK, gin.H{"code": http.StatusOK, "message": "用户创建成功"})
}
Delete user business logic
// DeleteUser 处理删除用户请求的函数
func DeleteUser(c *gin.Context) {
userIDStr := c.Param("id") // Assuming the route has "id" parameter
var User model.User
userID, err := strconv.ParseInt(userIDStr, 10, 64)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"code": http.StatusBadRequest, "error": "Failed to delete user, invalid user ID"})
return
}
//var user model.User
config.GVA_DB.Take(&User, userID)
ret := config.GVA_DB.Select("Student").Delete(&User)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"code": http.StatusInternalServerError, "error": fmt.Sprintf("Failed to delete user, unable to delete user: %v", err)})
return
}
c.JSON(http.StatusOK, gin.H{"code": http.StatusOK, "message": fmt.Sprintf("User deleted successfully, RowsAffected: %v", ret.RowsAffected)})
}
renew
Since this is the main table, logically just update the table and write the logic to change the corresponding table later.
Mainly to change the avatar and so on
Find
// GetUser 处理获取单个用户请求的函数
func GetUser(c *gin.Context) {
userIDStr := c.Param("id") // Assuming the route has "id" parameter
u := query.User
userID, err := strconv.ParseInt(userIDStr, 10, 64)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"code": http.StatusBadRequest, "error": "Failed to get user, invalid user ID"})
return
}
user, err := query.User.WithContext(context.Background()).Where(query.User.UserID.Eq(int32(userID))).Preload(u.Student, u.Teacher).First()
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"code": http.StatusInternalServerError, "error": fmt.Sprintf("Failed to get user, unable to get user: %v", err)})
return
}
c.JSON(http.StatusOK, gin.H{"code": http.StatusOK, "user": user})
}
// GetAllUsers 处理获取所有用户请求的函数
func GetAllUsers(c *gin.Context) {
u := query.User
users, err := query.User.WithContext(context.Background()).Preload(u.Student, u.Teacher).Find()
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"code": http.StatusInternalServerError, "error": fmt.Sprintf("Failed to get all users, unable to get user list: %v", err)})
return
}
c.JSON(http.StatusOK, gin.H{"code": http.StatusOK, "users": users})
}