在Golang中使用Gin和Gorm实现创建与更新操作共用同一结构体

在Golang中使用Gin和Gorm实现创建与更新操作共用同一结构体 免责声明:本人是Go语言新手

我的用户结构体如下:

type User struct {
	ID        uint32    `json:"id"`
	FirstName string    `json:"firstName" binding:"required"`
	LastName  string    `json:"lastName"`
	Email     string    `json:"email" binding:"required,email,uniqueModelValue=users email"`
	Active    bool      `json:"active"`
	Password  string    `json:"password,omitempty" binding:"required,gte=8"`
	UserType  string    `json:"userType" binding:"oneof=admin backoffice principal staff parent student"`
	CreatedAt time.Time `json:"createdAt"`
	UpdatedAt time.Time `json:"updatedAt"`
}

/POST /users 处理程序

    func Create(ctx *gin.Context) {
    	user := models.User{}
    	//验证
    	if err := ctx.ShouldBind(&user); err != nil {
    		response.Error(ctx, err)
    		return
    
    	}
    	db, _ := database.GetDB()
    	db.Create(&user)
    	// 从响应中移除密码
    	user.Password = ""
    	response.Success(ctx, user)
    }

我想使用相同的结构体创建一个更新处理程序,有没有办法可以使用相同的结构体来实现?

请注意,结构体在许多字段(如firstNameemail等)上都有必需的绑定标签。而在更新时,我可能不会传递这些字段。

我想出了类似这样的方案:

/PUT /users/ 处理程序

    func Update(ctx *gin.Context) {
    	userID := ctx.Param("userId")
    	user := models.User{}
    	db, _ := database.GetDB()
    	if err := db.First(&user, userID).Error; err != nil {
    		response.Error(ctx, err)
    		return
    	}
    	updateUser := models.User{}
    	if err := ctx.BindJSON(&updateUser); err != nil {
    		response.Error(ctx, err)
    	}
    	//fmt.Printf("%v", updateUser)
    	db.Model(&user).Updates(updateUser)
    	response.Success(ctx, user)
    }

这显然会失败,因为缺少必需的验证。例如,如果我尝试只更新lastName


更多关于在Golang中使用Gin和Gorm实现创建与更新操作共用同一结构体的实战教程也可以访问 https://www.itying.com/category-94-b0.html

1 回复

更多关于在Golang中使用Gin和Gorm实现创建与更新操作共用同一结构体的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


在Golang中,使用Gin和Gorm实现创建与更新操作共用同一结构体时,可以通过自定义验证标签或使用结构体标签控制验证行为。以下是解决方案:

1. 创建自定义验证标签

为更新操作添加omitempty验证标签:

type User struct {
    ID        uint32    `json:"id"`
    FirstName string    `json:"firstName" binding:"required,omitempty"`
    LastName  string    `json:"lastName" binding:"omitempty"`
    Email     string    `json:"email" binding:"required,email,uniqueModelValue=users email,omitempty"`
    Active    bool      `json:"active"`
    Password  string    `json:"password,omitempty" binding:"required,gte=8,omitempty"`
    UserType  string    `json:"userType" binding:"oneof=admin backoffice principal staff parent student,omitempty"`
    CreatedAt time.Time `json:"createdAt"`
    UpdatedAt time.Time `json:"updatedAt"`
}

2. 使用ShouldBindJSON替代ShouldBind

在更新处理程序中使用ShouldBindJSON并配合binding:"-"标签:

type UserUpdate struct {
    FirstName string `json:"firstName" binding:"-"`
    LastName  string `json:"lastName" binding:"-"`
    Email     string `json:"email" binding:"-"`
    Active    *bool  `json:"active" binding:"-"`
    Password  string `json:"password,omitempty" binding:"-"`
    UserType  string `json:"userType" binding:"-"`
}

func Update(ctx *gin.Context) {
    userID := ctx.Param("userId")
    db, _ := database.GetDB()
    
    // 获取现有用户
    existingUser := models.User{}
    if err := db.First(&existingUser, userID).Error; err != nil {
        response.Error(ctx, err)
        return
    }
    
    // 绑定更新数据
    updateData := UserUpdate{}
    if err := ctx.ShouldBindJSON(&updateData); err != nil {
        response.Error(ctx, err)
        return
    }
    
    // 手动更新非零值字段
    if updateData.FirstName != "" {
        existingUser.FirstName = updateData.FirstName
    }
    if updateData.LastName != "" {
        existingUser.LastName = updateData.LastName
    }
    if updateData.Email != "" {
        existingUser.Email = updateData.Email
    }
    if updateData.Active != nil {
        existingUser.Active = *updateData.Active
    }
    if updateData.Password != "" {
        existingUser.Password = updateData.Password
    }
    if updateData.UserType != "" {
        existingUser.UserType = updateData.UserType
    }
    
    // 保存更新
    if err := db.Save(&existingUser).Error; err != nil {
        response.Error(ctx, err)
        return
    }
    
    // 从响应中移除密码
    existingUser.Password = ""
    response.Success(ctx, existingUser)
}

3. 使用map[string]interface{}进行部分更新

func Update(ctx *gin.Context) {
    userID := ctx.Param("userId")
    db, _ := database.GetDB()
    
    // 获取现有用户
    existingUser := models.User{}
    if err := db.First(&existingUser, userID).Error; err != nil {
        response.Error(ctx, err)
        return
    }
    
    // 使用map接收更新数据
    updateData := make(map[string]interface{})
    if err := ctx.ShouldBindJSON(&updateData); err != nil {
        response.Error(ctx, err)
        return
    }
    
    // 移除不应更新的字段
    delete(updateData, "id")
    delete(updateData, "createdAt")
    delete(updateData, "updatedAt")
    
    // 如果密码为空,移除密码字段
    if password, ok := updateData["password"]; ok && password == "" {
        delete(updateData, "password")
    }
    
    // 执行更新
    if err := db.Model(&existingUser).Updates(updateData).Error; err != nil {
        response.Error(ctx, err)
        return
    }
    
    // 重新查询获取完整数据
    db.First(&existingUser, userID)
    existingUser.Password = ""
    response.Success(ctx, existingUser)
}

4. 使用指针字段处理可选更新

type User struct {
    ID        uint32     `json:"id"`
    FirstName *string    `json:"firstName,omitempty" binding:"required"`
    LastName  *string    `json:"lastName,omitempty"`
    Email     *string    `json:"email,omitempty" binding:"required,email,uniqueModelValue=users email"`
    Active    *bool      `json:"active,omitempty"`
    Password  *string    `json:"password,omitempty" binding:"required,gte=8"`
    UserType  *string    `json:"userType,omitempty" binding:"oneof=admin backoffice principal staff parent student"`
    CreatedAt time.Time  `json:"createdAt"`
    UpdatedAt time.Time  `json:"updatedAt"`
}

func Update(ctx *gin.Context) {
    userID := ctx.Param("userId")
    db, _ := database.GetDB()
    
    // 获取现有用户
    existingUser := models.User{}
    if err := db.First(&existingUser, userID).Error; err != nil {
        response.Error(ctx, err)
        return
    }
    
    // 绑定更新数据
    updateUser := models.User{}
    if err := ctx.ShouldBindJSON(&updateUser); err != nil {
        response.Error(ctx, err)
        return
    }
    
    // 使用Updates自动处理指针字段
    if err := db.Model(&existingUser).Updates(&updateUser).Error; err != nil {
        response.Error(ctx, err)
        return
    }
    
    // 重新查询获取完整数据
    db.First(&existingUser, userID)
    if existingUser.Password != nil {
        existingUser.Password = nil
    }
    response.Success(ctx, existingUser)
}

这些方法允许你在创建和更新操作中使用同一结构体,同时处理更新时的可选字段验证问题。

回到顶部