两个模型之间的多个关联 - Rails 5

时间:2017-10-12 02:47:10

标签: ruby-on-rails

我试图为Rails 5应用程序编写以下情况。 我有这两个型号:

class User < ApplicationRecord
  has_many :mastered_games, class_name: 'Game', foreign_key: 'game_id'
  has_and_belongs_to_many :played_games, class_name: 'Game', foreign_key: 'game_id'
end

class Game < ApplicationRecord
  belongs_to :dungeon_master, class_name: 'User', foreign_key: 'user_id'
  has_and_belongs_to_many :players, class_name: 'User', foreign_key: 'user_id'
end

这样的一般想法是任何用户都可以拥有许多玩过和掌握过的游戏,而且任何给定的游戏都只属于一个用户(一个DungeonMaster)并且有很多用户在玩游戏(玩家)。我知道使用模型名称可能会更容易,但这更具有词汇性,此外,关联可能会相互冲突。

我的迁移现在看起来像这样:

class CreateGames < ActiveRecord::Migration[5.1]
  def change
    create_table :games do |t|
      t.integer :game_id
      t.string :secret_key
      t.belongs_to :dungeon_master, index: true
      t.timestamps
    end
  end
end

class CreateUsers < ActiveRecord::Migration[5.1]
  def change
    create_table :users do |t|
      t.integer :user_id
      t.string :name
      t.string :email
      t.string :picture
      t.string :provider
      t.string :uid
      t.timestamps
    end
  end
end

class CreateGamesUsersTable < ActiveRecord::Migration[5.1]
  def change
    create_join_table :games, :users do |t|
      t.index :game_id
      t.index :user_id
    end
  end
end

出于某种原因,这对我来说似乎不起作用。例如,当我尝试将一个dungeon_master添加到rails控制台中的游戏时,如下所示:

u = User.new(name: 'Jon')
g = Game.new()
g.dungeon_master = u

这将返回错误ActiveModel::MissingAttributeError: can't write unknown attribute user_id。 此外,当我将一个用户添加到游戏中时,我遇到了多对多关联的双向性问题:

g.players << u

添加了用户但是当我尝试访问该用户的play_games时,它没有返回任何游戏。我可能遗漏了一些东西,但我无法弄清楚是什么。 任何帮助将不胜感激。

谢谢!

1 个答案:

答案 0 :(得分:0)

你有一些方向性翻转。具有belongs_to的任何模型都将在数据库表中接收外键。现在,您有一个games表,其中包含game_id列。你想要的是一个带有gamesuser_id列的dungeon_master_id表(老实说,使用默认的外键名称而不是在模型中手动指定要容易得多):

create_table :games do |t|
  t.references :dungeon_master 
  # other columns
end

create_table :users do |t|
  # other columns
end

create_join_table(:games, :users)

然后你的模型有点简单:

class User < ApplicationRecord
  has_many :mastered_games, class_name: 'Game', inverse_of: :dungeon_master
  has_and_belongs_to_many :played_games, class_name: 'Game'
end

class Game < ApplicationRecord
  belongs_to :dungeon_master, class_name: 'User', inverse_of: :mastered_games
  has_and_belongs_to_many :players, class_name: 'User'
end

您不应该对以下内容有任何疑问:

user = User.new(name: 'Jon')
game = Game.new(dungeon_master: user)