Golang中gorm如何避免在BindJson时转换ID字段

Golang中gorm如何避免在BindJson时转换ID字段 我正在使用以下结构体,并且不希望GORM因ID主键重复而报错。因此,我尝试了以下方法来在创建方法中忽略ID字段。即,我创建了另一个专门用于创建的结构体,GORM成功地忽略了传入的ID值。但我需要获取新创建记录的ID。我本以为GORM会将其放入GetID字段,但它并没有在其中放入任何ID。

上述方法的问题:我无法知道新创建记录的ID。如果我能以某种方式知道,我会在完整的用户结构体中获取该记录,并在响应中返回给用户,但这并没有发生。

问题:我试图在创建时忽略传入的ID。似乎有两种可能的解决方案:要么让GORM在创建时忽略传入的ID,并在字段中返回新的ID;要么让Gin上下文的BindJson忽略该值,不将其绑定到用户结构体。任何帮助都将不胜感激。谢谢。

type UserBase struct {
  ID           uint `gorm:"primary_key"`
  Email        string `validate:"required,email"`
  Password     string
  LanguageId   int16
}

type UserCreateUpdate struct {
  UserBase
  ID           uint `gorm:"-",json:"id"`
  GetID           uint `gorm:"column:id",json:"id"`
  // Language language_schema.Language `json:",omitempty"`
}

type User struct {
  UserBase
  CreatedAt    time.Time
  UpdatedAt    *time.Time // As it can be null thats why the pointer
  Language language_schema.Language `json:",omitempty"`
}

// And this is create method
db := schema_utils.GetDB()

var user_create user_schema.UserCreateUpdate
c.BindJSON(&user_create)

validate := user_schema.SchemaValidator(web_api.GetValidator())
if ok, errors := schema_utils.ValidateInputs(user_create, validate); !ok {
  web_api.ValidationErrorResponse(errors, c)
} else {
  if result := db.Table("users").Create(&user_create); result.Error != nil {
    c.JSON(http.StatusUnprocessableEntity, gin.H{"error": result.Error})
  } else {
    var user user_schema.User
    fmt.Println("created")
    fmt.Println(user_create.ID)
    fmt.Println(user_create.GetID)
    db.Where("id = ?", user_create.GetID).Table("users").Preload("Language").First(&user)
    c.JSON(200, user)
  }
}

更多关于Golang中gorm如何避免在BindJson时转换ID字段的实战教程也可以访问 https://www.itying.com/category-94-b0.html

1 回复

更多关于Golang中gorm如何避免在BindJson时转换ID字段的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


在Golang中使用GORM时,可以通过以下几种方式避免在BindJson时转换ID字段并正确获取新创建的记录ID:

方法1:使用指针字段和gorm:"-"标签

type UserCreateUpdate struct {
    ID         *uint `gorm:"-" json:"id,omitempty"` // 使用指针并忽略GORM处理
    Email      string `validate:"required,email"`
    Password   string
    LanguageId int16
}

// 创建方法
func CreateUser(c *gin.Context) {
    db := schema_utils.GetDB()
    var userCreate user_schema.UserCreateUpdate
    
    if err := c.BindJSON(&userCreate); err != nil {
        c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
        return
    }
    
    // 显式设置ID为nil,确保GORM使用自增ID
    userCreate.ID = nil
    
    if result := db.Table("users").Create(&userCreate); result.Error != nil {
        c.JSON(http.StatusUnprocessableEntity, gin.H{"error": result.Error})
        return
    }
    
    // 获取新创建的记录
    var user user_schema.User
    db.Table("users").Preload("Language").First(&user, userCreate.ID)
    
    c.JSON(200, user)
}

方法2:使用单独的DTO结构体

// 创建专用的DTO结构体,不包含ID字段
type UserCreateDTO struct {
    Email      string `json:"email" validate:"required,email"`
    Password   string `json:"password"`
    LanguageId int16  `json:"languageId"`
}

// 创建方法
func CreateUser(c *gin.Context) {
    db := schema_utils.GetDB()
    var userDTO user_schema.UserCreateDTO
    
    if err := c.BindJSON(&userDTO); err != nil {
        c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
        return
    }
    
    // 转换为数据库模型
    user := user_schema.User{
        Email:      userDTO.Email,
        Password:   userDTO.Password,
        LanguageId: userDTO.LanguageId,
    }
    
    if result := db.Table("users").Create(&user); result.Error != nil {
        c.JSON(http.StatusUnprocessableEntity, gin.H{"error": result.Error})
        return
    }
    
    // 预加载关联数据
    db.Table("users").Preload("Language").First(&user, user.ID)
    
    c.JSON(200, user)
}

方法3:使用GORM的Select方法排除字段

type UserCreateUpdate struct {
    ID         uint   `gorm:"primary_key" json:"id,omitempty"`
    Email      string `validate:"required,email"`
    Password   string
    LanguageId int16
}

// 创建方法
func CreateUser(c *gin.Context) {
    db := schema_utils.GetDB()
    var userCreate user_schema.UserCreateUpdate
    
    if err := c.BindJSON(&userCreate); err != nil {
        c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
        return
    }
    
    // 使用Select排除ID字段
    if result := db.Table("users").
        Select("Email", "Password", "LanguageId").
        Create(&userCreate); result.Error != nil {
        c.JSON(http.StatusUnprocessableEntity, gin.H{"error": result.Error})
        return
    }
    
    // 获取完整记录
    var user user_schema.User
    db.Table("users").Preload("Language").First(&user, userCreate.ID)
    
    c.JSON(200, user)
}

方法4:使用Omit方法忽略字段

func CreateUser(c *gin.Context) {
    db := schema_utils.GetDB()
    var userCreate user_schema.UserCreateUpdate
    
    if err := c.BindJSON(&userCreate); err != nil {
        c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
        return
    }
    
    // 使用Omit忽略ID字段
    if result := db.Table("users").
        Omit("ID").
        Create(&userCreate); result.Error != nil {
        c.JSON(http.StatusUnprocessableEntity, gin.H{"error": result.Error})
        return
    }
    
    fmt.Println("Created user ID:", userCreate.ID)
    
    var user user_schema.User
    db.Table("users").Preload("Language").First(&user, userCreate.ID)
    
    c.JSON(200, user)
}

推荐方案

建议使用方法2(单独的DTO结构体),这是最清晰且符合关注点分离原则的方式:

// DTO用于接收请求
type UserCreateRequest struct {
    Email      string `json:"email" binding:"required,email"`
    Password   string `json:"password" binding:"required,min=6"`
    LanguageId int16  `json:"languageId"`
}

// 创建方法
func CreateUserHandler(c *gin.Context) {
    db := schema_utils.GetDB()
    var req user_schema.UserCreateRequest
    
    if err := c.ShouldBindJSON(&req); err != nil {
        c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
        return
    }
    
    // 验证
    validate := user_schema.SchemaValidator(web_api.GetValidator())
    if ok, errors := schema_utils.ValidateInputs(req, validate); !ok {
        web_api.ValidationErrorResponse(errors, c)
        return
    }
    
    // 创建用户记录
    user := user_schema.User{
        Email:      req.Email,
        Password:   req.Password,
        LanguageId: req.LanguageId,
    }
    
    if result := db.Create(&user); result.Error != nil {
        c.JSON(http.StatusInternalServerError, gin.H{"error": result.Error.Error()})
        return
    }
    
    // 加载关联数据
    db.Preload("Language").First(&user, user.ID)
    
    c.JSON(http.StatusCreated, user)
}

这种方法完全避免了ID字段的绑定问题,同时能正确获取GORM自动生成的ID值。

回到顶部