涉及自连接“: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 :user
和 User
模型has_many :bands
但是如果我改用多对多(学生)关系,Band
模型 has_many :users, through :user_bands
加入 table 和 User
模型 has_many :bands, through :user_bands
加入table。 (在这种情况下,UserBands
模型有 belongs_to :user
和 belongs_to :band
)
但我同时需要这两种关系。我实际上并没有尝试将 has_many :bands
放入 User
模型中,同时将 has_many :users
(通过加入 table)和 belongs_to :users
放入 Bands 模型中,因为,除非Rails 比我认为的更神奇,它不会区分教师获得单对多而学生获得多对多。
我没有尝试我最好的猜测(如下),因为我承认我很胆小:我的数据库已经有大量完好无损且运行正常的多对多关系。有一次我试图在此过程的早期对其进行复杂的更改,它彻底把事情搞砸了,以至于回滚和撤消模型更改并没有让我回到我开始的地方,所以我不得不去在拔掉足够的头发进行剃度后,回到从头开始重建一切。这次我确实有 github,所以我 应该 能够在项目像以前一样失败时恢复项目,但 git 是它自己的挑剔雷区。
所以如果有人能先看看我的猜测,我将不胜感激。看起来对吗?我需要在更新数据库模式之前进行更改吗?:
- 在
User
模型中,添加has_many :bands
.
- 在
Band
模型中,添加has_many :students, through :user_bands
;添加 belongs_to :teacher
- 在
create_bands
迁移中,添加 t.belongs_to :teacher
- 在
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_name
和 foreign_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
我的 :users
table 成功地自连接了所有必要的令人困惑的(对这个新手来说)代码和 table 做到这一点所必需的。 :users
的两组是:teachers
和:students
。
我需要让 :teachers
组与 :bands
table 一对多加入(一个乐队可能只有一个老师),同时加入 :students
与 :bands
table 多对多(一个乐队可能有很多学生,反之亦然)。
让我感到困惑的是 :students
和 :teachers
都是 :users
。因此,如果我暂时假装只有一种用户并建立一对多(教师)关系,那么 Band
模型 belongs_to :user
和 User
模型has_many :bands
但是如果我改用多对多(学生)关系,Band
模型 has_many :users, through :user_bands
加入 table 和 User
模型 has_many :bands, through :user_bands
加入table。 (在这种情况下,UserBands
模型有 belongs_to :user
和 belongs_to :band
)
但我同时需要这两种关系。我实际上并没有尝试将 has_many :bands
放入 User
模型中,同时将 has_many :users
(通过加入 table)和 belongs_to :users
放入 Bands 模型中,因为,除非Rails 比我认为的更神奇,它不会区分教师获得单对多而学生获得多对多。
我没有尝试我最好的猜测(如下),因为我承认我很胆小:我的数据库已经有大量完好无损且运行正常的多对多关系。有一次我试图在此过程的早期对其进行复杂的更改,它彻底把事情搞砸了,以至于回滚和撤消模型更改并没有让我回到我开始的地方,所以我不得不去在拔掉足够的头发进行剃度后,回到从头开始重建一切。这次我确实有 github,所以我 应该 能够在项目像以前一样失败时恢复项目,但 git 是它自己的挑剔雷区。
所以如果有人能先看看我的猜测,我将不胜感激。看起来对吗?我需要在更新数据库模式之前进行更改吗?:
- 在
User
模型中,添加has_many :bands
. - 在
Band
模型中,添加has_many :students, through :user_bands
;添加belongs_to :teacher
- 在
create_bands
迁移中,添加t.belongs_to :teacher
- 在
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_name
和 foreign_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