HABTM and Fixtures

First off, I've done some googling and read some very informative stuff, but I'm having an issue and I can't figure out what I'm doing wrong. I've got it working with one model, but the other one is choking.. Here's the relevant data (and my current stuff):

player.rb: class Player < ActiveRecord::Base   has_and_belongs_to_many :friends,     :class_name => "Player", :association_foreign_key => "friend_id", :join_table => "friends_players"

  has_and_belongs_to_many :leagues,     :class_name => "Player", :association_foreign_key=>"player_id", :join_table=>"players_leagues" end

complex setup, but let me try to sort it out a bit:

player.rb: (the friends thing has nothing to do with the problem, so i don't include it)

has_and_belongs_to_many :leagues

that's default stuff, so no need to include it, makes code less readable: :class_name => "Player" :association_foreign_key=>"player_id", :join_table=>"players_leagues"

league.rb: has_many :players,     :class_name => "Player", :association_foreign_key => "player_id", :join_table => "players_leagues"

if i get your setup right, the wrong thing here is the has_many it should be habtm as in players

has_and_belongs_to_many :players

again the rest of definition isn't necessary, since you keep with naming conventions.

leagues.yml: league1:   player_id: user1 <= ??? why that ???   players: user1, user2, user3

is there another player_id in leagues? if so, define it as: belongs_to :players otherwise kick that line

i hope i understood you setup, otherwise it may be helpful to have a few more details of the associations and the exact fieldnames in all three used tables

Thanks Thorsten. :slight_smile:

Well the reason that league has a player_id is that is the "owner" of the league. He's by default a player in the league as well. Right now that's the last problem.. I had to create leagues_players (I guess players_leagues isn't the convention) and modified league and player to these:

class League < ActiveRecord::Base   has_and_belongs_to_many :players     #:class_name => "Player", :association_foreign_key => "player_id", :join_table => "players_leagues" end

class Player < ActiveRecord::Base   has_and_belongs_to_many :friends    # :class_name => "Player", :association_foreign_key => "friend_id", :join_table => "friends_players"

  has_and_belongs_to_many :leagues end

leagues.yml is currently

league1:   name: Test League   player_id: user1   playercount: 8   players: user1, user2, user3

now the rake task runs without error, and I get players being inserted in leagues_players correctly. However the player_id is not set (the owner of the league).

ok, so we have the complicated part. for the owner just define the association as well:

player.rb:

has_one :league

league.rb: belongs_to :player

in this case i would go for renaming this like belongs_to :owner, :foreign_key => player_id this will make you code more readable later on since you'll acces your league like

@league.players and @league.player

which can be confusing so the combination @league.players @league.owner would work better

Thanks again Thorsten, some more frustration on my end though. Adding the "belongs_to" has broken the friends association, somehow.

class League < ActiveRecord::Base   has_and_belongs_to_many :players     #:class_name => "Player", :association_foreign_key => "player_id", :join_table => "players_leagues"

  belongs_to :owner, :foreign_key => :player_id end

class Player < ActiveRecord::Base   has_and_belongs_to_many :friends   # :class_name => "Player", :association_foreign_key => "friend_id", :join_table => "friends_players"

  # If I comment this line out, it doesn't complain, but the leagues owner id (player_id) doesn't get set   # if I uncomment it, I get an error.   #has_many :leagues, :foreign_key=> player_id end

The error I get is: rake aborted! Mysql::Error: #42S22Unknown column 'friends' in 'field list': INSERT INTO `players` (`name`, `network_id`, `friends`) VALUES ('Jason', 'dbg_12345', 'user2, user3')

For further reference, the schema

  create_table "friends_players", :id => false, :force => true do |t|     t.integer "player_id"     t.integer "friend_id"     t.datetime "created_at"     t.datetime "updated_at"   end

  add_index "friends_players", ["friend_id"], :name => "index_friends_players_on_friend_id"   add_index "friends_players", ["player_id"], :name => "index_friends_players_on_player_id"

  create_table "leagues", :force => true do |t|     t.string "name"     t.integer "playercount"     t.integer "player_id"     t.datetime "timestarted"     t.datetime "created_at"     t.datetime "updated_at"   end

  create_table "leagues_players", :id => false, :force => true do |t|     t.integer "league_id"     t.integer "player_id"     t.datetime "created_at"     t.datetime "updated_at"   end

  add_index "leagues_players", ["league_id"], :name => "index_leagues_players_on_league_id"   add_index "leagues_players", ["player_id"], :name => "index_leagues_players_on_player_id"

  create_table "players", :force => true do |t|     t.string "name"     t.string "network_id"     t.datetime "created_at"     t.datetime "updated_at"   end

To followup my own message, I had to simplify a bit to get it working

class League < ActiveRecord::Base   has_and_belongs_to_many :players

  belongs_to :player # trying to do :owner, :foreign_key here caused an error in the parse end

class Player < ActiveRecord::Base   has_and_belongs_to_many :friends

  has_many :leagues end

league1:   name: Test League   player: user1 < -- Had to change to player instead of player_id   playercount: 8   players: user1, user2, user3

Thanks a lot for all of your help Thorsten.

Based on what I understand of your requirements, this doesn't seem quite right.

Players don't have many leagues, each player belongs to one league, and may have at most one league (as it's owner). Those are two separate relationships, so should be modeled separately.

I think you want something like:

class League < ActiveRecord::Base    has_many :players    belongs_to :owner, :class_name => 'Player'    # this implies that the 'leagues' table has an owner_id field end

class Player < ActiveRecord::Base    belongs_to :league    has_one :owned_league, :class_name => 'League', :foreign_key => :owner_id

end

leagues.yml

league1:     name: TestLeague     owner: player1a league

league2:     name: TestLeague2     owner: player2a

players.yml    player1a:         league: league1    player1b:         league: league1    player1c:        league: league1   player2a:       league: league2   player2b:       league: league2 ...

Of course the player names can be whatever you want, I picked player1a etc. just to make the relelationships clear

Ya, the difference was that players (human players, not the members of the team) can own multiple leagues and play in mulitple leagues.