Rails ActiveRecord 两个字段各自引用相同的 table

Rails ActiveRecord two fields each referencing the same table

我有一个 Player class,我想拥有 high_school_teamclub_team 属性。所以我认为 Player 将具有指向相应团队的 high_school_team_idclub_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'
...

HighSchoolTeamClubTeam 是使用 Team.

进行单一 table 继承的模型

我不明白为什么会收到错误消息。 docs 似乎说 t.referenes 的第一个参数是 table_name,第二个参数是 ref_name:team 是 table 的名称,我希望引用是 high_school_team_idclub_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_idplayers.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