如何使用 Go 保存和检索 Gorm 的多对多关系
How to save and retrieve Gorm's many2many relationship using Go
我想创建一个休息 API 来管理使用 Gorm 和 Gin 的转换。当我在我的两个模型之间添加关系并将这些项目提交到 API 时,我无法在它们之间建立预期的关系。 BornLocations
和 Nationalities
字段为空。我在这里错过了什么吗?
我的问题是:如何在我的回复中存储和检索该关系?
我的模特:
type Cast struct {
ID string `sql:"type:uuid;primary_key;default:uuid_generate_v4()"`
FullName string `gorm:"size:150;not null" json:"full_name"`
NickNames string `gorm:"size:250;null;" json:"nick_names"`
BornLocation Country `gorm:"many2many:CastBornLocation;association_foreignkey:ID;foreignkey:ID" json:"born_location"`
Nationalities []Country `gorm:"many2many:Castnationalities;association_foreignkey:ID;foreignkey:ID" json:"cast_nationalities"`
MiniBio string `gorm:"size:1000;null;" json:"mini_bio"`
UserRefer string
}
type Country struct {
ID string `sql:"type:uuid;primary_key;default:uuid_generate_v4()"`
Title string `gorm:"size:100;not null" json:"title"`
CreatedAt time.Time `gorm:"default:CURRENT_TIMESTAMP" json:"created_at"`
UpdatedAt time.Time `gorm:"default:CURRENT_TIMESTAMP" json:"updated_at"`
}
这是控制器:
func (server *Server) CreateCast(c *gin.Context) {
errList = map[string]string{}
item := models.Cast{}
err = json.Unmarshal(body, &item)
if err != nil {
errList["Unmarshal_error"] = "Cannot unmarshal body"
c.JSON(http.StatusUnprocessableEntity, gin.H{
"status": http.StatusUnprocessableEntity,
"error": errList,
})
return
}
// check the token
uid, err := auth.ExtractTokenID(c.Request)
if err != nil {
errList["Unauthorized"] = "Unauthorized"
c.JSON(http.StatusUnauthorized, gin.H{
"status": http.StatusUnauthorized,
"error": errList,
})
return
}
user := models.User{}
err = server.DB.Debug().Model(models.User{}).Where("id = ?", uid).Take(&user).Error
if err != nil {
errList["Unauthorized"] = "Unauthorized"
c.JSON(http.StatusUnauthorized, gin.H{
"status": http.StatusUnauthorized,
"error": errList,
})
return
}
item.UserRefer = uid
item.Prepare()
errorMessages := item.Validate("")
if len(errorMessages) > 0 {
errList = errorMessages
c.JSON(http.StatusUnprocessableEntity, gin.H{
"status": http.StatusUnprocessableEntity,
"error": errList,
})
return
}
itemCreated, err := item.SaveCast(server.DB) //Saving the cast!
if err != nil {
formattedError := formaterror.FormatError(err.Error())
errList = formattedError
c.JSON(http.StatusInternalServerError, gin.H{
"status": http.StatusInternalServerError,
"error": errList,
})
return
}
c.JSON(http.StatusCreated, gin.H{
"status": http.StatusCreated,
"response": itemCreated,
})
}
这是我要提交给 API 的 JSON 正文:
{
"full_name": "Cast full_name",
"nick_names": "nickname1, nickname2",
"born_location": {
"ID" :"caf2d6af-c360-4777-85d4-32541094b8be"
},
"cast_nationalities": [
{
"ID" :"caf2d6af-c360-4777-85d4-32541094b8be"
},
{
"ID" :"064058f4-f4ea-4d28-ad22-f9cf92df3513"
}
],
"mini_bio": "this is the mini bio of the cast"
}
现在,这是回应:
{
"response": {
"ID": "09f6a184-9b6e-4487-85f0-c2dbbce7ee5b",
"full_name": "Cast full_name",
"nick_names": "nickname1, nickname2",
"born": "",
"born_location": {
"ID": "e5c431ed-3158-4532-81b6-52d78e5539dc",
"title": "",
"created_at": "2020-07-16T13:23:32.579892866Z",
"updated_at": "2020-07-16T13:23:32.578216004Z",
"UserRefer": ""
},
"cast_nationalities": [
{
"ID": "caf2d6af-c360-4777-85d4-32541094b8be",
"title": "",
"created_at": "0001-01-01T00:00:00Z",
"updated_at": "2020-07-16T13:23:32.580620065Z",
"UserRefer": ""
},
{
"ID": "064058f4-f4ea-4d28-ad22-f9cf92df3513",
"title": "",
"created_at": "0001-01-01T00:00:00Z",
"updated_at": "2020-07-16T13:23:32.58249818Z",
"UserRefer": ""
}
],
"mini_bio": "this is the mini bio of the cast",
"bio": "",
"avatar": "",
"height": "",
"weight": "",
"created_at": "2020-07-16T13:23:32.576892187Z",
"updated_at": "2020-07-16T13:23:32.576892346Z",
"UserRefer": "c0223913-8679-4103-bcb6-66db0b2fed21"
},
"status": 201
}
这是获取所有演员表的控制器和响应:
func (server *Server) GetCasts(c *gin.Context) {
//clear previous error if any
errList = map[string]string{}
item := models.Cast{}
// check the token
uid, err := auth.ExtractTokenID(c.Request)
if err != nil {
errList["Unauthorized"] = "Unauthorized"
c.JSON(http.StatusUnauthorized, gin.H{
"status": http.StatusUnauthorized,
"error": errList,
})
return
}
items, err := item.FindCasts(server.DB, uid)
if err != nil {
errList["No_Items"] = "No Items Found"
c.JSON(http.StatusInternalServerError, gin.H{
"status": http.StatusInternalServerError,
"error": errList,
})
return
}
c.JSON(http.StatusOK, gin.H{
"status": http.StatusOK,
"response": items,
})
}
func (u *Cast) FindCasts(db *gorm.DB, uid string) (*[]Cast, error) {
var err error
casts := []Cast{}
err = db.Debug().Model(&Cast{}).Where("user_refer = ?", uid).Limit(100).Find(&casts).Error
if err != nil {
return &[]Cast{}, err
}
return &casts, err
}
演员表的最终回复:
{
"response": [
{
"ID": "09f6a184-9b6e-4487-85f0-c2dbbce7ee5b",
"full_name": "Cast full_name",
"nick_names": "nickname1, nickname2",
"born": "",
"born_location": {
"ID": "",
"title": "",
"created_at": "0001-01-01T00:00:00Z",
"updated_at": "0001-01-01T00:00:00Z",
"UserRefer": ""
},
"nationalities": null,
"mini_bio": "this is the mini bio of the cast",
"bio": "",
"avatar": "",
"height": "",
"weight": "",
"created_at": "2020-07-16T13:23:32.576892Z",
"updated_at": "2020-07-16T13:23:32.576892Z",
"UserRefer": "c0223913-8679-4103-bcb6-66db0b2fed21"
}
],
"status": 200
要获取数据,您可以在 gorm 中使用预加载子数据
err = db.Preload("BornLocation").Preload("Nationalities").Model(&Cast{})
.Where("user_refer = ?", uid).Limit(100).Find(&casts).Error
或者你可以使用标志
db.Set("gorm:auto_preload", true).Find(&casts).Error
这里你BornLocation
好像是OneToMany你可以这样做
type Cast struct {
...
BornLocationId string
BornLocation Country `gorm:"association_foreignkey:ID;foreignkey:BornLocationId" json:"born_location"`
...
}
我找到问题了!对于这个特定问题,我必须添加两件事。
首先,我必须在模型的准备函数中添加关系标识符。
u.BornLocation = Country{}
然后,为了保存演员实体,我不得不将其更改为:
func (u *Cast) SaveCast(db *gorm.DB) (*Cast, error) {
var err error
err = db.Debug().Create(&u).Error
if err != nil {
return &Cast{}, err
}
if u.ID != 0 {
err = db.Debug().Model(&Country{}).Where("id = ?", u.BornLocationId).Take(&u.Country).Error
if err != nil {
return &Cast{}, err
}
}
return u, nil
}
现在,检索所有实体:
err = db.Set("gorm:auto_preload", true).Find(&casts).Error
我想创建一个休息 API 来管理使用 Gorm 和 Gin 的转换。当我在我的两个模型之间添加关系并将这些项目提交到 API 时,我无法在它们之间建立预期的关系。 BornLocations
和 Nationalities
字段为空。我在这里错过了什么吗?
我的问题是:如何在我的回复中存储和检索该关系?
我的模特:
type Cast struct {
ID string `sql:"type:uuid;primary_key;default:uuid_generate_v4()"`
FullName string `gorm:"size:150;not null" json:"full_name"`
NickNames string `gorm:"size:250;null;" json:"nick_names"`
BornLocation Country `gorm:"many2many:CastBornLocation;association_foreignkey:ID;foreignkey:ID" json:"born_location"`
Nationalities []Country `gorm:"many2many:Castnationalities;association_foreignkey:ID;foreignkey:ID" json:"cast_nationalities"`
MiniBio string `gorm:"size:1000;null;" json:"mini_bio"`
UserRefer string
}
type Country struct {
ID string `sql:"type:uuid;primary_key;default:uuid_generate_v4()"`
Title string `gorm:"size:100;not null" json:"title"`
CreatedAt time.Time `gorm:"default:CURRENT_TIMESTAMP" json:"created_at"`
UpdatedAt time.Time `gorm:"default:CURRENT_TIMESTAMP" json:"updated_at"`
}
这是控制器:
func (server *Server) CreateCast(c *gin.Context) {
errList = map[string]string{}
item := models.Cast{}
err = json.Unmarshal(body, &item)
if err != nil {
errList["Unmarshal_error"] = "Cannot unmarshal body"
c.JSON(http.StatusUnprocessableEntity, gin.H{
"status": http.StatusUnprocessableEntity,
"error": errList,
})
return
}
// check the token
uid, err := auth.ExtractTokenID(c.Request)
if err != nil {
errList["Unauthorized"] = "Unauthorized"
c.JSON(http.StatusUnauthorized, gin.H{
"status": http.StatusUnauthorized,
"error": errList,
})
return
}
user := models.User{}
err = server.DB.Debug().Model(models.User{}).Where("id = ?", uid).Take(&user).Error
if err != nil {
errList["Unauthorized"] = "Unauthorized"
c.JSON(http.StatusUnauthorized, gin.H{
"status": http.StatusUnauthorized,
"error": errList,
})
return
}
item.UserRefer = uid
item.Prepare()
errorMessages := item.Validate("")
if len(errorMessages) > 0 {
errList = errorMessages
c.JSON(http.StatusUnprocessableEntity, gin.H{
"status": http.StatusUnprocessableEntity,
"error": errList,
})
return
}
itemCreated, err := item.SaveCast(server.DB) //Saving the cast!
if err != nil {
formattedError := formaterror.FormatError(err.Error())
errList = formattedError
c.JSON(http.StatusInternalServerError, gin.H{
"status": http.StatusInternalServerError,
"error": errList,
})
return
}
c.JSON(http.StatusCreated, gin.H{
"status": http.StatusCreated,
"response": itemCreated,
})
}
这是我要提交给 API 的 JSON 正文:
{
"full_name": "Cast full_name",
"nick_names": "nickname1, nickname2",
"born_location": {
"ID" :"caf2d6af-c360-4777-85d4-32541094b8be"
},
"cast_nationalities": [
{
"ID" :"caf2d6af-c360-4777-85d4-32541094b8be"
},
{
"ID" :"064058f4-f4ea-4d28-ad22-f9cf92df3513"
}
],
"mini_bio": "this is the mini bio of the cast"
}
现在,这是回应:
{
"response": {
"ID": "09f6a184-9b6e-4487-85f0-c2dbbce7ee5b",
"full_name": "Cast full_name",
"nick_names": "nickname1, nickname2",
"born": "",
"born_location": {
"ID": "e5c431ed-3158-4532-81b6-52d78e5539dc",
"title": "",
"created_at": "2020-07-16T13:23:32.579892866Z",
"updated_at": "2020-07-16T13:23:32.578216004Z",
"UserRefer": ""
},
"cast_nationalities": [
{
"ID": "caf2d6af-c360-4777-85d4-32541094b8be",
"title": "",
"created_at": "0001-01-01T00:00:00Z",
"updated_at": "2020-07-16T13:23:32.580620065Z",
"UserRefer": ""
},
{
"ID": "064058f4-f4ea-4d28-ad22-f9cf92df3513",
"title": "",
"created_at": "0001-01-01T00:00:00Z",
"updated_at": "2020-07-16T13:23:32.58249818Z",
"UserRefer": ""
}
],
"mini_bio": "this is the mini bio of the cast",
"bio": "",
"avatar": "",
"height": "",
"weight": "",
"created_at": "2020-07-16T13:23:32.576892187Z",
"updated_at": "2020-07-16T13:23:32.576892346Z",
"UserRefer": "c0223913-8679-4103-bcb6-66db0b2fed21"
},
"status": 201
}
这是获取所有演员表的控制器和响应:
func (server *Server) GetCasts(c *gin.Context) {
//clear previous error if any
errList = map[string]string{}
item := models.Cast{}
// check the token
uid, err := auth.ExtractTokenID(c.Request)
if err != nil {
errList["Unauthorized"] = "Unauthorized"
c.JSON(http.StatusUnauthorized, gin.H{
"status": http.StatusUnauthorized,
"error": errList,
})
return
}
items, err := item.FindCasts(server.DB, uid)
if err != nil {
errList["No_Items"] = "No Items Found"
c.JSON(http.StatusInternalServerError, gin.H{
"status": http.StatusInternalServerError,
"error": errList,
})
return
}
c.JSON(http.StatusOK, gin.H{
"status": http.StatusOK,
"response": items,
})
}
func (u *Cast) FindCasts(db *gorm.DB, uid string) (*[]Cast, error) {
var err error
casts := []Cast{}
err = db.Debug().Model(&Cast{}).Where("user_refer = ?", uid).Limit(100).Find(&casts).Error
if err != nil {
return &[]Cast{}, err
}
return &casts, err
}
演员表的最终回复:
{
"response": [
{
"ID": "09f6a184-9b6e-4487-85f0-c2dbbce7ee5b",
"full_name": "Cast full_name",
"nick_names": "nickname1, nickname2",
"born": "",
"born_location": {
"ID": "",
"title": "",
"created_at": "0001-01-01T00:00:00Z",
"updated_at": "0001-01-01T00:00:00Z",
"UserRefer": ""
},
"nationalities": null,
"mini_bio": "this is the mini bio of the cast",
"bio": "",
"avatar": "",
"height": "",
"weight": "",
"created_at": "2020-07-16T13:23:32.576892Z",
"updated_at": "2020-07-16T13:23:32.576892Z",
"UserRefer": "c0223913-8679-4103-bcb6-66db0b2fed21"
}
],
"status": 200
要获取数据,您可以在 gorm 中使用预加载子数据
err = db.Preload("BornLocation").Preload("Nationalities").Model(&Cast{})
.Where("user_refer = ?", uid).Limit(100).Find(&casts).Error
或者你可以使用标志
db.Set("gorm:auto_preload", true).Find(&casts).Error
这里你BornLocation
好像是OneToMany你可以这样做
type Cast struct {
...
BornLocationId string
BornLocation Country `gorm:"association_foreignkey:ID;foreignkey:BornLocationId" json:"born_location"`
...
}
我找到问题了!对于这个特定问题,我必须添加两件事。
首先,我必须在模型的准备函数中添加关系标识符。
u.BornLocation = Country{}
然后,为了保存演员实体,我不得不将其更改为:
func (u *Cast) SaveCast(db *gorm.DB) (*Cast, error) {
var err error
err = db.Debug().Create(&u).Error
if err != nil {
return &Cast{}, err
}
if u.ID != 0 {
err = db.Debug().Model(&Country{}).Where("id = ?", u.BornLocationId).Take(&u.Country).Error
if err != nil {
return &Cast{}, err
}
}
return u, nil
}
现在,检索所有实体:
err = db.Set("gorm:auto_preload", true).Find(&casts).Error