为了账号安全,请及时绑定邮箱和手机立即绑定

Gorm 多对多关系重复值

Gorm 多对多关系重复值

Go
九州编程 2022-08-01 19:08:04
我正在尝试在我的演示API中,在 技能和技能列表之间设置多对多关系。Job[]Skill作业结构type Job struct {    ID          string              `sql:"type:uuid;primary_key;"`    Title       string              `json:"title,omitempty"`    Skills      []*skill.Skill      `json:"skills,omitempty"gorm:"many2many:job_skill;"`    CreatedAt   time.Time    UpdatedAt   time.Time}技能结构type Skill struct {    gorm.Model    Name string `json:"name,omitempty"`}然后,我使用 自动生成联接表。gorm.DB.AutoMigrate()当我向我的 API 发送请求时,数据在第一次时就会正确创建,并且联接表会按预期方式填充。POST示例数据POST{    "title": "Senior Python Engineer",          "skills": [{"name": "javascript"}, {"name": "python"}]}但是,当我发送添加新技能的请求时,它会复制技能表中的技能,然后在联接表中为已存在的技能创建新记录。PATCH示例数据PATCH{    "title": "Lead Engineer",          "skills": [{"name": "javascript"}, {"name": "python"}, {"name": "management"}]}对数据执行 get 请求将显示以下内容:{    "title": "Lead Engineer",          "skills": [{"name": "javascript"}, {"name": "python"}, {"name": "javascript"}, {"name": "python"}, {"name": "management"}]}我也尝试过设置所击中的技能,但是当添加新技能时,它失败了,因为它说另外两个已经存在,这很好,但随后不会添加新的技能。gorm:"unique"Name我假设我只能发回新值?不是整个列表?为了清楚起见,我的一些Go代码func GetJobs(w http.ResponseWriter, r *http.Request) {    j := &[]Job{}    o := database.DB.Preload("Skills").Find(&j)    render.JSON(w, r, o)}func UpdateJob(w http.ResponseWriter, r *http.Request) {    j := &Job{}    err := json.NewDecoder(r.Body).Decode(j)    if err != nil {        return    }    o := database.DB.Model(&j).Where("id = ?", j.ID).Update(&j)    render.JSON(w, r, o)}
查看完整描述

2 回答

?
眼眸繁星

TA贡献1873条经验 获得超9个赞

这是Gorm联想的两件事,我觉得在其文档中没有充分传达,这继续让开发人员感到困惑。

1) 它使用 ID 来标识关联的实体

当您更新该技能列表时,如果它们中没有要添加的ID,则它们只是要添加的新实体。这会导致您的值重复。即使您有一个字段,如果该字段不是实体的主键,则 gorm 将尝试创建新记录并导致约束冲突。unique

有几种方法可以解决这个问题:

  • 确保 API 用户必须为相关实体提供 ID

  • 通过用户提供的其他代理键从数据库中提取实体 ID,并将其填充到要保存的实体中。在你的情况下,这可能是因为它是独一无二的。name

  • 使该代理键成为主键(使结构成为)。namegorm:"primaryKey"Skill

2) 更新时,它不会删除关联切片中未显示的现有关联

调用 Save/Update gorm 时,不会删除集合关联端的实体。这是一项安全功能,可避免在简单的保存/更新中意外删除数据。你必须明确地想要这种行为。

要解决此问题,您可以使用关联模式替换集合,作为更新的一部分:。db.Model(&job).Association('Skills').Replace(&job.Skills)


查看完整回答
反对 回复 2022-08-01
?
繁花如伊

TA贡献2012条经验 获得超12个赞

详细阐述Eziquel的答案,并展示什么对我有用。


我更新了我的技能结构,将 用作主键Name


type Skill struct {

    Name string `json:"name,omitempty" gorm:"primary_key"`

}

然后,我阅读了一些文档,只是使用不确定它的作用,但是没有它,关联就会复制或未更新。gorm.DB.Session(&gorm.Session{FullSaveAssociations: true}).Updates(&j)


func UpdateJob(w http.ResponseWriter, r *http.Request) {

    j := &Job{}

    err := json.NewDecoder(r.Body).Decode(j)

    if err != nil {

        return

    }


    database.DB.Session(&gorm.Session{FullSaveAssociations: true}).Updates(&j)

    database.DB.Model(&j).Association("Skills").Replace(&j.Skills)


    render.JSON(w, r, &j)

}

我不知道该怎么办,但我看到Jihnzu说要在文档中使用它,没有进一步的解释。结合使用,请确保没有重复项,并且关联会更新。database.DB.Session(&gorm.Session{FullSaveAssociations: true}).Updates(&j).Association("Skills").Replace(&j.Skills)


如果有多个,你会做的:


database.DB.Session(&gorm.Session{FullSaveAssociations: true}).Updates(&j)

database.DB.Model(&j).Association("Skills").Replace(&j.Skills)

database.DB.Model(&j).Association("Locations").Replace(&j.Locations)


查看完整回答
反对 回复 2022-08-01
  • 2 回答
  • 0 关注
  • 207 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信