【问题标题】:Rails ActiveRecord two fields each referencing the same tableRails ActiveRecord 两个字段,每个字段都引用同一个表
【发布时间】:2020-01-31 00:14:12
【问题描述】:

我有一个 Player 类,我想拥有 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 进行单表继承的模型。

我不明白为什么会出现错误。 docs 似乎说t.referenes 的第一个参数是table_name 和第二个ref_name:team 是表的名称,我希望引用为 high_school_team_idclub_team_id

当我将参数的顺序切换到t.references 时,它仍然不起作用。不知何故,它给出了同样的错误:you can't define an already defined column 'team_id'.

【问题讨论】:

    标签: ruby-on-rails rails-activerecord


    【解决方案1】:

    看来您混淆了SchemaStatement#add_referenceTableDefinition#references,它们的签名完全不同。

    如果您想设置一个无法从列名(第一个参数)派生表的外键列,您只需传递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 正如其他答案所推荐的那样,并不等同。这只是向列添加索引,但没有外键约束。

    然后您可以将 Player 上的关联设置为:

    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
    

    但首先真正更好的选择是设置一个连接表:

    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
    

    【讨论】:

    • add_reference 的等效调用是 add_reference(:teams, :high_school_team, foreign_key: {to_table: :teams })
    【解决方案2】:

    您提到的文档谈到了需要添加对现有表的引用的情况。

    用于添加对新表的引用:

    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
    

    【讨论】:

      猜你喜欢
      • 2020-12-29
      • 2010-12-31
      • 2021-06-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-09-10
      • 1970-01-01
      相关资源
      最近更新 更多