Golang中GORM多对多关系如何添加额外字段
Golang中GORM多对多关系如何添加额外字段 我对GORM还不太熟悉,不太理解其后台的工作原理。
我有一个名为 user 的表和一个名为 user_type 的表,我正在尝试创建一个用户。
type User struct {
// .... 这里有一些字段
AccountId *uuid.UUID `json:"foreignid,omitempty" gorm:"column:account_id" immutable:"-"`
TypeIds []uuid.UUID `json:"typeids,omitempty" gorm:"-:all"`
Types []Type `json:"-" gorm:"many2many:user_type;"`
}
type Type struct {
// .... 这里有一些字段
TypeIds []uuid.UUID `json:"typeids,omitempty" gorm:"-:all"`
Types []Type `json:"-" gorm:"many2many:type_on_type;foreignKey:id;joinForeignKey:type_id;References:id;joinReferences:inner_type_id"`
}
type UserType struct {
UserId *uuid.UUID `json:"user" gorm:"column:user_id" immutable:"-" validate:"required"`
TypeId *string `json:"type" gorm:"column:type_id"`
Type *string `json:"type" gorm:"column:type"`
}
func (rcv *User) Create(context *gin.Context, body []byte) (interface{}, *mn.Error) {
resourceSlice := reflect.New(reflect.MakeSlice(reflect.SliceOf(rcv.resourceType), 0, 0).Type())
if goErr := json.Unmarshal(body, resourceSlice.Interface()); goErr != nil {
return nil, mn.ErrorJSONUnmarshal.Clone(goErr.Error())
}
kindTags = rcv.GetKindTags() // 我在这里有 type 列的最新值
_ := tx.SetupJoinTable(&rpg.User{}, "Types", &UserTag{})
result := db.Create(resourceSlice.Elem().Interface())
if result.Error != nil {
return nil,result.Error
}
return resourceSlice.Elem().Interface(), nil
}
当调用 Create() 时,会显示运行了两个查询。
1. INSERT INTO "data"."user_type" ("user_id","type_id")
2. INSERT INTO "data"."user" ()
我尝试在 user_type 表中添加一个额外的列 type。
为此,我添加了一个 hook。
func (ut *UserType) BeforeCreate(db *gorm.DB) error {
db.Statement.SetColumn("type", ut.Type) // 需要在这里更新值
return nil
}
在此之后,我可以看到查询中添加了 type 列,但值为 null。
INSERT INTO “user_type” (“user_id”,“type_id”,“type”) VALUES (‘3091a78e-5a3c-6ae2-f36b-a9af1aa3982b’,‘ed31a1ce-8805-62fb-e7bf-fb751d6beb73’,NULL) ON CONFLICT DO NOTHING
但是我无法使用包含列值的 kindTags 来为 ut.Type 填充更新后的值。
你能告诉我如何将值传递给 hook 吗?
谢谢
更多关于Golang中GORM多对多关系如何添加额外字段的实战教程也可以访问 https://www.itying.com/category-94-b0.html
进行如下更改:
kindTags := rcv.GetKindTags() // 在此处获取 kindTags
// 假设 kindTags 是一个映射或具有某种结构以获取 Type 值
// 设置连接表
_ = tx.SetupJoinTable(&User{}, "Types", &UserType{})
// 使用 resourceSlice 来设置用户和类型
users := resourceSlice.Elem().Interface().([]User)
for i := range users {
for j := range users[i].Types {
users[i].Types[j].Type = kindTags[users[i].Types[j].TypeId] // 或者根据你的方式获取值
}
}
func (ut *UserType) BeforeCreate(tx *gorm.DB) error {
// 在此处基于某些逻辑设置 Type 值
// 例如,如果 kindTags 在上下文或全局变量中可用
ut.Type = "someValueFromKindTags" // 将此替换为获取 kindTags 值的实际逻辑
return nil
}
更多关于Golang中GORM多对多关系如何添加额外字段的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
在GORM中为多对多关系的连接表添加额外字段,需要正确设置关联模型和预加载。以下是解决方案:
// 定义UserType模型作为连接表
type UserType struct {
UserID *uuid.UUID `gorm:"primaryKey;column:user_id"`
TypeID *uuid.UUID `gorm:"primaryKey;column:type_id"`
Type string `gorm:"column:type"` // 额外字段
CreatedAt time.Time
UpdatedAt time.Time
}
// 修改User模型
type User struct {
ID *uuid.UUID `gorm:"primaryKey;type:uuid;default:gen_random_uuid()"`
Name string
Types []Type `gorm:"many2many:user_type;joinForeignKey:UserID;JoinReferences:TypeID"`
}
// 修改Type模型
type Type struct {
ID *uuid.UUID `gorm:"primaryKey;type:uuid;default:gen_random_uuid()"`
Name string
Users []User `gorm:"many2many:user_type;joinForeignKey:TypeID;JoinReferences:UserID"`
}
// 在初始化时设置连接表
func init() {
db, _ := gorm.Open(postgres.Open(dsn), &gorm.Config{})
// 注册连接表
err := db.SetupJoinTable(&User{}, "Types", &UserType{})
if err != nil {
panic(err)
}
err = db.SetupJoinTable(&Type{}, "Users", &UserType{})
if err != nil {
panic(err)
}
}
// 创建用户并设置额外字段
func CreateUserWithType(db *gorm.DB, userName string, typeIDs []uuid.UUID, typeValue string) error {
user := &User{
Name: userName,
Types: []Type{},
}
// 查找类型
var types []Type
if err := db.Where("id IN ?", typeIDs).Find(&types).Error; err != nil {
return err
}
// 创建用户
if err := db.Create(user).Error; err != nil {
return err
}
// 手动创建关联记录并设置额外字段
for _, t := range types {
userType := &UserType{
UserID: user.ID,
TypeID: t.ID,
Type: typeValue, // 设置额外字段
}
if err := db.Create(userType).Error; err != nil {
return err
}
}
return nil
}
// 或者使用Association模式
func CreateUserWithAssociation(db *gorm.DB, userName string, types []Type, typeValue string) error {
user := &User{
Name: userName,
Types: types,
}
// 创建用户但不自动创建关联
if err := db.Omit("Types").Create(user).Error; err != nil {
return err
}
// 手动添加关联并设置额外字段
for _, t := range types {
userType := &UserType{
UserID: user.ID,
TypeID: t.ID,
Type: typeValue,
}
if err := db.Create(userType).Error; err != nil {
return err
}
}
return nil
}
// 查询时包含额外字段
func GetUserWithTypes(db *gorm.DB, userID uuid.UUID) (*User, error) {
var user User
// 预加载关联并选择额外字段
err := db.Preload("Types", func(db *gorm.DB) *gorm.DB {
return db.Joins("JOIN user_type ON user_type.type_id = types.id AND user_type.user_id = ?", userID)
}).First(&user, "id = ?", userID).Error
if err != nil {
return nil, err
}
// 如果需要获取连接表的额外字段
var userTypes []UserType
if err := db.Where("user_id = ?", userID).Find(&userTypes).Error; err != nil {
return nil, err
}
return &user, nil
}
对于你的具体问题,需要在创建关联时直接设置UserType模型的字段值,而不是依赖hook:
func (rcv *User) Create(context *gin.Context, body []byte) (interface{}, error) {
var user User
if err := json.Unmarshal(body, &user); err != nil {
return nil, err
}
// 创建用户
if err := db.Create(&user).Error; err != nil {
return nil, err
}
// 获取kindTags中的type值
kindTags := rcv.GetKindTags()
typeValue := kindTags["type"] // 假设这是你的type值
// 为每个TypeID创建关联记录
for _, typeID := range user.TypeIds {
userType := &UserType{
UserID: user.ID,
TypeID: &typeID,
Type: typeValue, // 直接设置额外字段
}
if err := db.Create(userType).Error; err != nil {
return nil, err
}
}
return user, nil
}
这样可以直接在创建UserType记录时设置type字段的值,而不需要通过hook传递。

