如何使用 Go 保存和检索 Gorm 的多对多关系

How to save and retrieve Gorm's many2many relationship using Go

我想创建一个休息 API 来管理使用 Gorm 和 Gin 的转换。当我在我的两个模型之间添加关系并将这些项目提交到 API 时,我无法在它们之间建立预期的关系。 BornLocationsNationalities 字段为空。我在这里错过了什么吗?

我的问题是:如何在我的回复中存储和检索该关系?

我的模特:

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