Rails ActiveRecord 两个字段各自引用相同的 table
Rails ActiveRecord two fields each referencing the same table
我有一个 Player
class,我想拥有 high_school_team
和 club_team
属性。所以我认为 Player
将具有指向相应团队的 high_school_team_id
和 club_team_id
属性。我尝试在以下迁移中执行此操作,但它不起作用。
class CreatePlayers < ActiveRecord::Migration[6.0]
def change
create_table :players do |t|
t.string :first_name
t.string :middle_name
t.string :last_name
t.decimal :height
t.decimal :weight
t.date :birthday
t.references :team, :high_school_team, foreign_key: true
t.references :team, :club_team, foreign_key: true
t.decimal :gpa
t.string :class_year
t.string :intended_major
t.string :email
t.string :phone_number
t.text :notes
t.timestamps
end
end
end
它给出了以下错误:
code/scout-db [master●] » rails db:migrate
== 20191218003854 CreatePlayers: migrating ====================================
-- create_table(:players)
rails aborted!
StandardError: An error has occurred, this and all later migrations canceled:
you can't define an already defined column 'team_id'.
/Library/Ruby/Gems/2.6.0/gems/activerecord-6.0.2.1/lib/active_record/connection_adapters/abstract/schema_definitions.rb:372:in `column'
...
HighSchoolTeam
和 ClubTeam
是使用 Team
.
进行单一 table 继承的模型
我不明白为什么会收到错误消息。 docs 似乎说 t.referenes
的第一个参数是 table_name
,第二个参数是 ref_name
。 :team
是 table 的名称,我希望引用是 high_school_team_id
和 club_team_id
.
当我将参数的顺序切换为t.references
时,它仍然不起作用。它以某种方式给出了相同的错误:you can't define an already defined column 'team_id'.
.
您提到的文档讨论了当您需要添加对现有 table 的引用时的情况。
添加对新 table 的引用:
t.references :team, :high_school_team, foreign_key: true
这段代码是错误的。相反,它应该是
t.references :high_school_team, foreign_key: {to_table: :teams}
to_table
需要加database referential integrity
所以你的迁移将是这样的:
class CreatePlayers < ActiveRecord::Migration[6.0]
def change
create_table :players do |t|
....
t.references :high_school_team, foreign_key: {to_table: :teams}
t.references :club_team, foreign_key: {to_table: :teams}
....
end
end
end
看起来你混淆了SchemaStatement#add_reference
and TableDefinition#references
,它们具有完全不同的签名。
如果你想设置一个外键列,其中 table 不能从列名(第一个参数)派生,你只需传递 foreign_key: { to_table: :teams}
.
class CreatePlayers < ActiveRecord::Migration[6.0]
def change
create_table :players do |t|
t.references :high_school_team, foreign_key: { to_table: :teams}
t.references :club_team, foreign_key: { to_table: :teams}
end
end
end
其他答案推荐的 t.references :high_school_team, index: true
不等价。这只是向列添加索引但没有外键约束。
然后您可以将播放器上的关联设置为:
class Player < ApplicationRecord
belongs_to :high_school_team, class_name: 'Team'
belongs_to :club_team, class_name: 'Team'
end
您不能在另一端使用单个 has_many :players
关联,因为外键列可以是 players.high_school_team_id
或 players.club_team_id
.
class Team
has_many :high_school_team_players,
foreign_key: :high_school_team_id,
class_name: 'Player'
has_many :club_team_players,
foreign_key: :club_team_id,
class_name: 'Player'
end
但首先真正更好的选择是设置连接 table:
class Player
has_many :placements
has_one :high_school_team,
through: :placements,
source: :team
class_name: 'Team'
has_one :club_team,
through: :placements,
source: :team
class_name: 'Team'
end
class Placement
belongs_to :player
belongs_to :team
end
class Team
has_many :placements
has_many :players, through: :placements
end
我有一个 Player
class,我想拥有 high_school_team
和 club_team
属性。所以我认为 Player
将具有指向相应团队的 high_school_team_id
和 club_team_id
属性。我尝试在以下迁移中执行此操作,但它不起作用。
class CreatePlayers < ActiveRecord::Migration[6.0]
def change
create_table :players do |t|
t.string :first_name
t.string :middle_name
t.string :last_name
t.decimal :height
t.decimal :weight
t.date :birthday
t.references :team, :high_school_team, foreign_key: true
t.references :team, :club_team, foreign_key: true
t.decimal :gpa
t.string :class_year
t.string :intended_major
t.string :email
t.string :phone_number
t.text :notes
t.timestamps
end
end
end
它给出了以下错误:
code/scout-db [master●] » rails db:migrate
== 20191218003854 CreatePlayers: migrating ====================================
-- create_table(:players)
rails aborted!
StandardError: An error has occurred, this and all later migrations canceled:
you can't define an already defined column 'team_id'.
/Library/Ruby/Gems/2.6.0/gems/activerecord-6.0.2.1/lib/active_record/connection_adapters/abstract/schema_definitions.rb:372:in `column'
...
HighSchoolTeam
和 ClubTeam
是使用 Team
.
我不明白为什么会收到错误消息。 docs 似乎说 t.referenes
的第一个参数是 table_name
,第二个参数是 ref_name
。 :team
是 table 的名称,我希望引用是 high_school_team_id
和 club_team_id
.
当我将参数的顺序切换为t.references
时,它仍然不起作用。它以某种方式给出了相同的错误:you can't define an already defined column 'team_id'.
.
您提到的文档讨论了当您需要添加对现有 table 的引用时的情况。
添加对新 table 的引用:
t.references :team, :high_school_team, foreign_key: true
这段代码是错误的。相反,它应该是
t.references :high_school_team, foreign_key: {to_table: :teams}
to_table
需要加database referential integrity
所以你的迁移将是这样的:
class CreatePlayers < ActiveRecord::Migration[6.0]
def change
create_table :players do |t|
....
t.references :high_school_team, foreign_key: {to_table: :teams}
t.references :club_team, foreign_key: {to_table: :teams}
....
end
end
end
看起来你混淆了SchemaStatement#add_reference
and TableDefinition#references
,它们具有完全不同的签名。
如果你想设置一个外键列,其中 table 不能从列名(第一个参数)派生,你只需传递 foreign_key: { to_table: :teams}
.
class CreatePlayers < ActiveRecord::Migration[6.0]
def change
create_table :players do |t|
t.references :high_school_team, foreign_key: { to_table: :teams}
t.references :club_team, foreign_key: { to_table: :teams}
end
end
end
其他答案推荐的 t.references :high_school_team, index: true
不等价。这只是向列添加索引但没有外键约束。
然后您可以将播放器上的关联设置为:
class Player < ApplicationRecord
belongs_to :high_school_team, class_name: 'Team'
belongs_to :club_team, class_name: 'Team'
end
您不能在另一端使用单个 has_many :players
关联,因为外键列可以是 players.high_school_team_id
或 players.club_team_id
.
class Team
has_many :high_school_team_players,
foreign_key: :high_school_team_id,
class_name: 'Player'
has_many :club_team_players,
foreign_key: :club_team_id,
class_name: 'Player'
end
但首先真正更好的选择是设置连接 table:
class Player
has_many :placements
has_one :high_school_team,
through: :placements,
source: :team
class_name: 'Team'
has_one :club_team,
through: :placements,
source: :team
class_name: 'Team'
end
class Placement
belongs_to :player
belongs_to :team
end
class Team
has_many :placements
has_many :players, through: :placements
end