Gorm多对多关系复制值

Gorm多对多关系复制值,go,go-gorm,Go,Go Gorm,我正在尝试在演示API中建立一个多对多关系,在作业和技能列表[]技能之间 作业结构 类型作业结构{ ID字符串`sql:“类型:uuid;主键;”` 标题字符串`json:“标题,省略为空”` Skills[]*skill.skill`json:“Skills,省略空”gorm:“many2many:job\u skill;”` 时间,时间 时间到了,时间到了 } 技能结构 类型技能结构{ 戈姆模型 名称字符串`json:“名称,省略为空”` } 然后我使用gorm.DB.AutoMigra

我正在尝试在演示API中建立一个多对多关系,在
作业
和技能列表[]技能之间

作业结构

类型作业结构{
ID字符串`sql:“类型:uuid;主键;”`
标题字符串`json:“标题,省略为空”`
Skills[]*skill.skill`json:“Skills,省略空”gorm:“many2many:job\u skill;”`
时间,时间
时间到了,时间到了
}
技能结构

类型技能结构{
戈姆模型
名称字符串`json:“名称,省略为空”`
}
然后我使用
gorm.DB.AutoMigrate()
自动生成联接表

当我向我的API发送一个
POST
请求时,第一次就正确地创建了数据,并且按照预期填充了联接表

示例
POST
数据

{
“职称”:“高级Python工程师”,
“技能”:[{“名称”:“javascript”},{“名称”:“python”}]
}
但是,当我发送
补丁
请求以添加新技能时,它会复制技能表中的技能,然后在连接表中为已经存在的技能创建新记录

示例
补丁
数据

{
“职务”:“首席工程师”,
“技能”:[{“名称”:“javascript”},{“名称”:“python”},{“名称”:“管理”}]
}
对数据执行get请求将显示以下内容:

{
“职务”:“首席工程师”,
“技能”:[{“名称”:“javascript”},{“名称”:“python”},{“名称”:“javascript”},{“名称”:“python”},{“名称”:“管理”}]
}
我也尝试过在技能名上设置
gorm:“unique”
,但是当添加一个新技能时失败了,因为它说其他两个技能已经存在,这很好,但不会添加新技能

我假设我只能发回新的值?不是全部名单

为了清晰起见,我的一些Go代码

func GetJobs(w http.ResponseWriter,r*http.Request){
j:=&[]作业{}
o:=database.DB.Preload(“Skills”).Find(&j)
JSON(w,r,o)
}
func UpdateJob(w http.ResponseWriter,r*http.Request){
j:=&作业{}
err:=json.NewDecoder(r.Body).Decode(j)
如果错误!=零{
返回
}
o:=database.DB.Model(&j).Where(“id=?”,j.id).Update(&j)
JSON(w,r,o)
}

我觉得gorm文档中没有充分传达这两个与gorm相关的东西,这继续让开发人员感到困惑

1) 它使用ID来标识关联的实体 当你更新技能列表时,如果他们没有gorm的ID,他们只是要添加的新实体。这将导致重复的值。即使您有一个
唯一的
字段,如果该字段不是实体的主键,gorm也会尝试创建一个新记录,并导致违反约束

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

  • 确保API用户必须为相关实体提供ID
  • 通过用户提供的其他代理项键从数据库中取出实体ID,并在数据库中填充这些ID以保存实体。在您的情况下,这可能是
    名称
    ,因为它是唯一的
  • 将该代理键设置为主键(将
    技能的
    结构设置为
    gorm:“primaryKey”
2) 更新时,它不会删除未出现在关联切片中的现有关联 调用“保存/更新”时,gorm不会删除集合关联远端的实体。这是一项安全功能,可避免在简单保存/更新时意外删除数据。你必须明确表示想要那种行为


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

来详细说明Eziquel的答案,并说明什么对我有效

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

类型技能结构{
名称字符串`json:“名称,省略空”gorm:“主键”`
}
然后,我阅读了一些文档,说只使用
gorm.DB.Session(&gorm.Session{FullSaveAssociations:true})。更新(&j)
不确定它做什么,但没有它,关联会重复或不更新

func UpdateJob(w http.ResponseWriter,r*http.Request){
j:=&作业{}
err:=json.NewDecoder(r.Body).Decode(j)
如果错误!=零{
返回
}
database.DB.Session(&gorm.Session{FullSaveAssociations:true})。更新(&j)
database.DB.Model(&j).Association(“Skills”).Replace(&j.Skills)
JSON(w、r和j)
}
我不知道
database.DB.Session(&gorm.Session{FullSaveAssociations:true})。Updates(&j)
做了什么,但我看到Jihnzu说在文档中使用它,没有进一步的解释。将其与
.Association(“Skills”).Replace(&j.Skills)
结合使用,确保没有重复项,并且关联会更新

如果有不止一个,您会:

database.DB.Session(&gorm.Session{FullSaveAssociations:true})。更新(&j)
database.DB.Model(&j).Association(“Skills”).Replace(&j.Skills)
database.DB.Model(&j).Association(“位置”).Replace(&j.Locations)

可靠的答案谢谢!我不确定情况是否如此。理想情况下,我希望在新技能到来时动态添加新技能,因此我想我将尝试第1部分中的第3点。如果做不到这一点,我将尝试包含一个ID,看看它是如何运行的。
FullSaveAssociations
选项让gorm尝试更新关联实体的数据以及实体之间的链接(在多对多的情况下,可以将其视为仅更新作业技能联接表,或者另外更新技能表)。代码的缺点是,第一次呼叫将在