Rails 5 更新 has_many 通过关联模型

Rails 5 update has_many through associates models

我有以下型号

class TeamPlayer < ApplicationRecord
  belongs_to :team
  belongs_to :player
  belongs_to :role
end

class Team < ApplicationRecord
  has_many :team_players
  has_many :players, :through => :team_players
end

class Role < ApplicationRecord
  has_many :team_players
  has_many :players, :through => :team_players
end

class Player < ApplicationRecord
  has_many :team_players
  has_many :teams, :through => :team_players

  has_many :roles, :through => :team_players
end

基本上,我想为团队中的不同球员分配不同的角色。

id  team_id     player_id   role_id 
2   1           2           1   
3   3           2           1   
4   1           1           2   

在我的 teams_controller.rb 中添加具有角色的新玩家、更新具有新角色的玩家以及从我的团队中删除该玩家应该是什么样子?

这只是一个可能解决方案的开始,它与添加了一些模型和数据库验证的解决方案非常相似。其中一些验证可确保每个 three-way 关系 (FilledTeamRole) 的唯一性,因此需要处理尝试创建重复记录的错误,或者您可以过滤每个 [=] 的可能 ID 93=] 可以选择以便无法创建重复项。

一个完整的解决方案将取决于您希望 TeamPlayerRole class 之间的其他关联,而不是需要所有三个的关联。例如,你是否 want/need TeamPlayer 之间的关联,其中仅存在这两个 class 之间的关系而无需 Role (TeamPlayer id: 1, team_id: 1, player_id: 1)。如果需要这些关系,则需要额外的代码来实现这一点,我已经拥有并且可以作为建议提供。

就控制器的外观而言,您可以使用 filled_team_roles 控制器(或者创建一个仪表板控制器),提供实例变量 @teams@players@roles 为表单中的每个 class 填充 drop-down 菜单,以创建 filled_team_roles 关系。您还可以在每个其他 classes 中包含其他表单,其中使用两个 drop-downs 而不是三个,第三个值是 class 的选定模型 ID,其控制器是在(例如 players_controller 中的 edit 操作与 teamrole 的 drop-downs)

~/app/models/team.rb

class Team < ApplicationRecord
  has_many :filled_team_roles, dependent: :destroy
  validates :name, uniqueness: { scope: [:sport, :city] }
  scope :by_name_asc, -> { order(name: :asc) }
end

~/app/models/player.rb

class Player < ApplicationRecord
  has_many :filled_team_roles, dependent: :destroy
  validates_uniqueness_of :ssn
  scope :by_name_asc, -> { order(last_name: :asc, first_name: :asc) }
end

~/app/models/role.rb

class Role < ApplicationRecord
  has_many :filled_team_roles, dependent: :destroy
  validates_uniqueness_of :name
  scope :by_name_asc, -> { order(name: :asc) }
end

~/app/models/filled_team_role.rb

class FilledTeamRole < ApplicationRecord
  belongs_to :team
  belongs_to :player
  belongs_to :role
  validates :team_id,   presence: true
  validates :player_id, presence: true
  validates :role_id,   presence: true
  validates :team_id, uniqueness: { scope: [:player_id, :role_id] }    
end

~/db/migrate/20170127041000_create_team.rb

class CreateTeam < ActiveRecord::Migration[5.0]
  def change
    create_table :teams do |t|
      t.string :name
      t.string :sport
      t.string :city
      t.string :state
      t.string :country
      t.timestamps null: false
    end
    add_index :teams, [:name, :sport, :city], unique: true
  end
end

~/db/migrate/20170127041100_create_player.rb

class CreatePlayer < ActiveRecord::Migration[5.0]
  def change
    create_table :players do |t|
      t.string :first_name
      t.string :last_name, index: true
      t.string :full_name_surname_first
      t.string :ssn, index: { unique: true }
      t.timestamps null: false
    end
  end
end

~/db/migrate/20170127041200_create_role.rb

class CreateRole < ActiveRecord::Migration[5.0]
  def change
    create_table :roles do |t|
      t.string :name, index: { unique: true }
      t.timestamps null: false
    end
  end
end

~/db/migrate/20170127051300_create_filled_team_role.rb

class CreateFilledTeamRole < ActiveRecord::Migration[5.0]
  def change
    create_table :filled_team_roles do |t|
      t.timestamps null: false
      t.references :team
      t.references :role
      t.references :player
    end
    add_index :filled_team_roles,
             [:team_id, :player_id, :role_id],
               unique: true,
               name: 'index_filled_team_roles_unique_combination_of_foreign_keys'
  end
end

~/db/seeds.rb

Team.create(name: 'Los Angeles Dodgers', sport: 'baseball', city: 'Los Angeles', state: 'CA', country: 'United States')
Team.create(name: 'New York Yankees',    sport: 'baseball', city: 'New York',    state: 'NY', country: 'United States')
Team.create(name: 'Chicago Cubs',        sport: 'baseball', city: 'Chicago',     state: 'IL', country: 'United States')
Team.create(name: 'St. Louis Cardinals', sport: 'baseball', city: 'St. Louis',   state: 'MO', country: 'United States')
Player.create(first_name: 'Max',   last_name: 'Walker', full_name_surname_first: 'Walker, Max', ssn: '123-45-6789')
Player.create(first_name: 'Homer', last_name: 'Winn',   full_name_surname_first: 'Winn, Homer', ssn: '234-56-7890')
Player.create(first_name: 'Will',  last_name: 'Steel',  full_name_surname_first: 'Steel, Will', ssn: '345-67-8901')
Player.create(first_name: 'Lucky', last_name: 'Snag',   full_name_surname_first: 'Snag, Lucky', ssn: '456-78-9012')
Role.create(name: 'pitcher')
Role.create(name: 'catcher')
Role.create(name: 'first baseman')
Role.create(name: 'second baseman')
Role.create(name: 'shortstop')
Role.create(name: 'third baseman')
Role.create(name: 'right fielder')
Role.create(name: 'center fielder')
Role.create(name: 'left fielder')
FilledTeamRole.create(team_id: 1, player_id: 1, role_id: 1)
FilledTeamRole.create(team_id: 2, player_id: 2, role_id: 2)
FilledTeamRole.create(team_id: 3, player_id: 3, role_id: 3)
FilledTeamRole.create(team_id: 4, player_id: 4, role_id: 4)