go - Gorm 多对多关系重复值
问题描述
我试图在我的演示 API 中建立一个多对多的关系,在 aJob
和一个技能列表之间[]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"}]
}
对数据执行获取请求将显示以下内容:
{
"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)
}
解决方案
这是我认为在其文档中没有充分传达 gorm 关联的两件事,这继续使开发人员感到困惑。
1) 它使用 ID 来识别关联实体
当您更新该技能列表时,如果他们没有 ID 来确认他们只是要添加的新实体。这会导致您的重复值。即使您有一个unique
字段,如果该字段不是实体的主键,那么 gorm 将尝试创建新记录并导致违反约束。
有几种方法可以解决这个问题:
- 确保 API 用户必须为相关实体提供 ID
- 通过用户提供的其他代理键从数据库中提取实体 ID,并将其填充到您的要保存实体中。在您的情况下,这可能是
name
因为它是独一无二的。 - 使该代理键成为您的主键(使
name
您gorm:"primaryKey"
的Skill
结构成为)。
2) 更新时,不会删除未出现在关联切片中的现有关联
当您调用保存/更新时,gorm 不会删除集合关联远端的实体。这是一项安全功能,可避免在简单的保存/更新时意外删除数据。您必须明确表示想要这种行为。
为了解决这个问题,您可以使用关联模式将集合替换为更新的一部分:db.Model(&job).Association('Skills').Replace(&job.Skills)
.
推荐阅读
- 3d - pyopengl中用于旋转的pyquaternion
- javascript - 将 props 从父组件转发到子组件
- python - 雪花与 Sharepoint 的集成
- asp.net-core - 如何在 ASP.NET Core 5.0 中阻止其他数据
- python - 扫雷岛检测
- ruby-on-rails - 将雪花集成到 Ruby on rails
- vue.js - 在 main.js 文件中导入自定义 vue 组件
- python - 如何实现计数器以动态创建井字棋盘
- python - Pandas - 如何修改绘图栏中的历史图的宽度?
- python - 虚幻引擎 PIE 使用 Python 启动和停止