【问题标题】:Gorm Associations Many to ManyGorm 关联多对多
【发布时间】:2020-11-16 05:07:59
【问题描述】:

我知道 gorm 多对多关联会创建一个连接表,但是如果我想要/需要一个带有附加字段的自定义连接表怎么办

下面我的例子

所以我有两个 gorm 模型

type Exercise struct {
    gorm.Model
    Name        string `gorm:"not null" json:"name"`
    Description string `gorm:"not null json:"description"`
}
type Workout struct {
    gorm.Model
    Name string `gorm:"not null" json:"name"`
    CreatedAt time.Time `gorm:"not null" json:"created_at"`
}

如果我使用 gorm 多对多关联,它将创建一个锻炼锻炼连接表,但我将如何确保填写诸如 reps 和 set 之类的其他字段。这是需要在控制器中完成的事情,而不是真正的 gorm 处理。我用 Nodejs 和 sequelize 做过类似的事情,但对于 go 和 gorm 世界来说真的很新。

【问题讨论】:

    标签: database go go-gorm


    【解决方案1】:

    聚会有点晚了,但是……我的目标是创造类似的东西,花了一整天的时间,碰壁后,退后一步重新考虑……

    经过深思熟虑,很多游戏,以及朋友的好建议,决定从稍微不同的角度接近。

    如果您以不同的方式看待它,您可以将其“改写”为两个 has-one(或 belongs-to)和两个 one-to-many,而不是多对多:

    • 运动有很多锻炼-运动
    • 锻炼有很多锻炼-锻炼
    • workout-exercise 有锻炼和一项运动

    另外,在我的游戏中,我意识到多对多表的属性(列)在上述模型中的任何地方都没有引用,因此无论如何它都没有用。

    如果您将锻炼-锻炼视为“一流”模型,它开始变得更有意义并变得更实用:

    • 您可以创建练习 - 它是一种静态资源池
    • 您可以创建锻炼方式
    • 然后您可以使用锻炼-锻炼模型在锻炼中填充锻炼。

    总而言之,我的建议是这样的:

    type Exercise struct {
        gorm.Model
        Name        string `gorm:"not null" json:"name"`
        Description string `gorm:"not null" json:"description"`
        Workouts    []WorkoutExercise
    }
    
    type Workout struct {
        gorm.Model
        Name      string    `gorm:"not null" json:"name"`
        CreatedAt time.Time `gorm:"not null" json:"created_at"`
        Exercises []WorkoutExercise
    }
    
    type WorkoutExercise struct {
        gorm.Model
        WorkoutID  uint
        Workout    Workout `gorm:"foreignKey:WorkoutID;references:ID"`
        ExerciseID uint
        Exercise   Exercise `gorm:"foreignKey:ExerciseID;references:ID"`
        Sets       uint
        Reps       uint
        Weights    uint
    }
    

    它产生以下架构(我相信这是需要的):

    gym=# \dt
                   List of relations
     Schema |       Name        | Type  |   Owner   
    --------+-------------------+-------+-----------
     public | installations     | table | gym
     public | workout_exercises | table | gym
     public | workouts          | table | gym
    (3 rows)
    
    gym=# \d workouts
                                           Table "public.workouts"
       Column   |           Type           | Collation | Nullable |               Default                
    ------------+--------------------------+-----------+----------+--------------------------------------
     id         | bigint                   |           | not null | nextval('workouts_id_seq'::regclass)
     created_at | timestamp with time zone |           | not null | 
     updated_at | timestamp with time zone |           |          | 
     deleted_at | timestamp with time zone |           |          | 
     name       | text                     |           | not null | 
    Indexes:
        "workouts_pkey" PRIMARY KEY, btree (id)
        "idx_workouts_deleted_at" btree (deleted_at)
    Referenced by:
        TABLE "workout_exercises" CONSTRAINT "fk_workouts_exercises" FOREIGN KEY (workout_id) REFERENCES workouts(id)
    
    gym=# \d exercises
                                           Table "public.exercises"
       Column    |           Type           | Collation | Nullable |                Default                
    -------------+--------------------------+-----------+----------+---------------------------------------
     id          | bigint                   |           | not null | nextval('exercises_id_seq'::regclass)
     created_at  | timestamp with time zone |           |          | 
     updated_at  | timestamp with time zone |           |          | 
     deleted_at  | timestamp with time zone |           |          | 
     name        | text                     |           | not null | 
     description | text                     |           | not null | 
    Indexes:
        "exercises_pkey" PRIMARY KEY, btree (id)
        "idx_exercises_deleted_at" btree (deleted_at)
    Referenced by:
        TABLE "workout_exercises" CONSTRAINT "fk_exercises_workouts" FOREIGN KEY (exercise_id) REFERENCES exercises(id)
    
    gym=# \d workout_exercises
                                           Table "public.workout_exercises"
       Column    |           Type           | Collation | Nullable |                    Default                    
    -------------+--------------------------+-----------+----------+-----------------------------------------------
     id          | bigint                   |           | not null | nextval('workout_exercises_id_seq'::regclass)
     created_at  | timestamp with time zone |           |          | 
     updated_at  | timestamp with time zone |           |          | 
     deleted_at  | timestamp with time zone |           |          | 
     workout_id  | bigint                   |           |          | 
     exercise_id | bigint                   |           |          | 
     sets        | bigint                   |           |          | 
     reps        | bigint                   |           |          | 
     weights     | bigint                   |           |          | 
    Indexes:
        "workout_exercises_pkey" PRIMARY KEY, btree (id)
        "idx_workout_exercises_deleted_at" btree (deleted_at)
    Foreign-key constraints:
        "fk_exercises_workouts" FOREIGN KEY (exercise_id) REFERENCES exercises(id)
        "fk_workouts_exercises" FOREIGN KEY (workout_id) REFERENCES workouts(id)
    

    如果您考虑一下,将练习拉到锻炼中,而没有其他属性是没有意义的。如果您只想获得锻炼的练习,可以从中间模型中提取它*:

    workout := Workout
    db.Preload("WorkoutExercise.Exercise").First(&workout, 10)
    
    for _, we := range workout.Exercises {
        // access reps, etc directly from we
        // and access the exercise from the property we.Exercise
        log.Printf("do %d sets of %d repetitions of %s", we.Sets, we.Reps, 
                   we.Exercise.Name)
    }
    
    

    *免责声明 - 没有实际运行此代码(其余的工作)。请查看Nested Preloading 以实现一些深度嵌套的急切加载。

    【讨论】:

      【解决方案2】:
      type Exercise struct {
          gorm.Model
          Name        string `gorm:"not null" json:"name"`
          Description string `gorm:"not null json:"description"`
          Workouts []Workout `gorm:"many2many:exercise_workout;"`
      }
      type Workout struct {
          gorm.Model
          Name string `gorm:"not null" json:"name"`
          CreatedAt time.Time `gorm:"not null" json:"created_at"`
      }
      

      many2many 的标记值是您定义的关联表名的名称。 并使用以下代码加载带有锻炼数据的练习。

      var exercises []Exercise
      db.Preload("Workouts").Find(&exercises) 
      

      如果你想用运动数据加载锻炼,只需在 Workout 结构中添加 many2many 关联,例如:

      type Exercise struct {
          gorm.Model
          Name        string `gorm:"not null" json:"name"`
          Description string `gorm:"not null json:"description"`
          Workouts []Workout `gorm:"many2many:exercise_workout;"`
      }
      type Workout struct {
          gorm.Model
          Name string `gorm:"not null" json:"name"`
          CreatedAt time.Time `gorm:"not null" json:"created_at"`
          Exercises []Exercise `gorm:"many2many:exercise_workout;"`
      }
      
      var workouts []Workout
      db.Preload("Exercises").Find(&workouts) 
      

      【讨论】:

      • 感谢您的回复。但是我将如何在连接表中添加其他字段。喜欢组数和次数?那会在控制器中完成吗?
      • 如果要自定义连接表。我创建了一个连接表模型并将您的列放在上面,例如 CreateAt 、 CreateUser ,并且必须包括像 ExerciseID 和 WorkoutID 这样的列(您还可以使用 gorm 标签选项 ForeignKey 和 AssociationForeignKey 自定义列名)。并在练习模型之前迁移连接模型。 gorm 将重新识别您的自定义连接模型和您的模型之间的关系
      • @DanielTAN 我想你不能举个例子吧?
      • 根本不回答问题
      • 不回答问题
      猜你喜欢
      • 1970-01-01
      • 2013-03-23
      • 2016-04-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-05-16
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多