涉及自连接“:users”table 和“:bands”table 的棘手 Rails 迁移和建模:同步的一对多和多对多关系

Tricky Rails migration and modeling involving a self-join ":users" table and a ":bands" table: simultaneous one-to-many and many-to-many relationships

我的 :users table 成功地自连接了所有必要的令人困惑的(对这个新手来说)代码和 table 做到这一点所必需的。 :users的两组是:teachers:students

我需要让 :teachers 组与 :bands table 一对多加入(一个乐队可能只有一个老师),同时加入 :students:bands table 多对多(一个乐队可能有很多学生,反之亦然)。

让我感到困惑的是 :students:teachers 都是 :users。因此,如果我暂时假装只有一种用户并建立一对多(教师)关系,那么 Band 模型 belongs_to :userUser 模型has_many :bands

但是如果我改用多对多(学生)关系,Band 模型 has_many :users, through :user_bands 加入 table 和 User 模型 has_many :bands, through :user_bands加入table。 (在这种情况下,UserBands 模型有 belongs_to :userbelongs_to :band

但我同时需要这两种关系。我实际上并没有尝试将 has_many :bands 放入 User 模型中,同时将 has_many :users(通过加入 table)和 belongs_to :users 放入 Bands 模型中,因为,除非Rails 比我认为的更神奇,它不会区分教师获得单对多而学生获得多对多。

我没有尝试我最好的猜测(如下),因为我承认我很胆小:我的数据库已经有大量完好无损且运行正常的多对多关系。有一次我试图在此过程的早期对其进行复杂的更改,它彻底把事情搞砸了,以至于回滚和撤消模型更改并没有让我回到我开始的地方,所以我不得不去在拔掉足够的头发进行剃度后,回到从头开始重建一切。这次我确实有 github,所以我 应该 能够在项目像以前一样失败时恢复项目,但 git 是它自己的挑剔雷区。

所以如果有人能先看看我的猜测,我将不胜感激。看起来对吗?我需要在更新数据库模式之前进行更改吗?:

  1. User模型中,添加has_many :bands.
  2. Band模型中,添加has_many :students, through :user_bands;添加 belongs_to :teacher
  3. create_bands 迁移中,添加 t.belongs_to :teacher
  4. UserBands 模型中,添加 belongs_to :teacher 并在 create_user_bands 迁移中添加 t.belongs_to :teacher

所需的协会不是自加入的。自连接主要用于从单个 table 构建树状层次结构 - 请参阅 the guides for a good example

您只需要在两个 table 之间建立多个关联 - 这里要记住的关键是您必须为每个关联使用唯一的名称。如果您声明多个具有相同名称的关联,则后者会毫无错误地覆盖前者。

教师

class AddTeacherIdToBands < ActiveRecord::Migration[5.2]
  def change
    add_reference :bands, :teacher, foreign_key: { to_table: :users }
  end
end

class User < ApplicationRecord
  has_many :bands_as_teacher, 
    class_name: 'Band',
    foreign_key: 'teacher_id'
end

class Band < ApplicationRecord
  belongs_to :teacher, 
    class_name: 'User'
end

我们将协会命名为bands_as_teacher以避免冲突和混淆。这需要我们显式设置 class_nameforeign_key 选项,因为它们不能从名称中推断出来。

这是您犯错并使您的解决方案过于复杂的地方。如果关联是一对多,则不需要涉及连接 table.

学生

要在乐队与其成员之间建立关联,您需要通过加入建立 m2m 关联 table:

class CreateBandMemberships < ActiveRecord::Migration[5.2]
  def change
    create_table :band_memberships do |t|
      t.references :band, foreign_key: true
      t.references :user, foreign_key: true
      t.timestamps
    end
  end
end

class Band < ApplicationRecord
  # ...
  has_many :band_memberships
  has_many :users, through: :band_memberships
end

class BandMembership < ApplicationRecord
  belongs_to :band
  belongs_to :user
end

class User < ApplicationRecord
  # ...
  has_many :band_memberships
  has_many :bands, through: :band_memberships
end

您可以通过提供 source 选项来改进命名,该选项告诉 Rails 在其连接通过的模型上使用哪个关联。

class Band < ApplicationRecord
  # ...
  has_many :band_memberships
  has_many :members, through: :band_memberships, source: :user
end

class User < ApplicationRecord
  # ...
  has_many :band_memberships
  has_many :bands_as_member, through: :band_memberships, source: :band
end