Forum: Ruby on Rails Confusion about has_many / belongs_to ...

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
Andrew C. (Guest)
on 2006-04-07 18:55
(Received via mailing list)
I have a table called players and its model Player, and I have a table
called games and its model Game.

In the games table I have:

player1_id int(11)
player2_id int(11)

I want the tables to be associated so that I can access the player
objects using table.player1 and table.player2 rather than having to
lookup the objects based on the ids.

I am not sure how to go about this but I suspect I need to use either
has_many or belongs_to or one of the mixed versions in my Game/Player
models.

I would be very grateful if someone could shed light on what I need to
do here.

Thank you
Andy
Brian V. Hughes (Guest)
on 2006-04-07 19:47
(Received via mailing list)
Well, if a game has more than one player, and your players can take part
in
more than one game (that's an assumption, so it could be wrong;), then
what
you really have is a has_and_belongs_to_many relationship. That would
give you
a games_players join table.

In fact, if you wanted to maintain separate player meta data, for each
game
they participated in, as well as game-specific meta data, you could
actually
structure this relationship as a has_many :through. This would probably
change
the games_players table into a participants table (or something
similar). And
you would put:

   has_many :games, :through => :participants # in the Player model
   has_many :players, :through => :participants # in the Game model

and in the Participant model

   belongs_to :game
   belongs_to :player

-Brian
Andrew C. (Guest)
on 2006-04-07 20:07
A player can only participate in a single game, which should make the
situation easy but I can't quite figure it out...

Does the Game model has_many :player ?

Does the Player model belongs_to :game?

and once thats sorted out would I be able to access those associated
objects with game.player1 and game.player2 ?

That is, does rails know that because games has player1_id and
player2_id that games.player1 and games.player2 are objects from the
players table?

You can see I am confused! ;)

-Andy



Brian V. Hughes wrote:
> Well, if a game has more than one player, and your players can take part
> in
> more than one game (that's an assumption, so it could be wrong;), then
> what
> you really have is a has_and_belongs_to_many relationship. That would
> give you
> a games_players join table.
>
> In fact, if you wanted to maintain separate player meta data, for each
> game
> they participated in, as well as game-specific meta data, you could
> actually
> structure this relationship as a has_many :through. This would probably
> change
> the games_players table into a participants table (or something
> similar). And
> you would put:
>
>    has_many :games, :through => :participants # in the Player model
>    has_many :players, :through => :participants # in the Game model
>
> and in the Participant model
>
>    belongs_to :game
>    belongs_to :player
>
> -Brian
Ryan B. (Guest)
on 2006-04-07 22:07
(Received via mailing list)
On Apr 7, 2006, at 9:07 AM, Andrew C. wrote:

> That is, does rails know that because games has player1_id and
> player2_id that games.player1 and games.player2 are objects from the
> players table?
>
> You can see I am confused! ;)
>
> -Andy


I believe what you actually need is a has_one relationship. Although
the game does have many players, the way the table is set up, it only
has one player1 and one player2. Try this:

class Game < ActiveRecord::Base
   has_one :player1, :class_name => 'Player'
   has_one :player2, :class_name => 'Player'
end

You need to specify the class name because otherwise it will look for
a class called Player1 which of course doesn't exist. You should then
be able to use game.player1 or game.player2 to access the players.

However, you may find this database design will cause problems later.
If, for example, you want to support more than two players, you will
need to add more columns for player3, player4, etc. Then what if you
want to support, say 16 players. That is a lot of columns and could
get messy! If this is at all a possibility, you should go with a true
one-to-many relationship.

You would do this by putting a game_id column in the players table
and removing the player1 and player2 columns from the games table.
You could then use the "has_many :players" relationship method in
your Game model. You can then grab the players with game.players[0]
or game.players[1], etc. This also allows you to iterate through all
players instead of having to refer to each one individually.

Ryan
Andrew C. (Guest)
on 2006-04-07 23:40
Ryan, I was really hoping your first suggestion would work as it sounded
like a great way to implement this being that I have no plans to expand
for more players down the road ( its intended for head-to-head only ).

I setup the model as suggested, identically and still have player1_id
and player2_id defined the players table, but now it seems game.player1
and game.player2 always refer to the same object, sort of like player2
is an alias of player1. I suspect has_one is causing this
relationship...

However, it appears I already did setup game to have a has_many
relationship with the players table and indeed I can access them via
game.players[0] and game.players[1] -- I had no idea I could so this is
a definite improvement and should prove useful.

Thank you very much, its all getting a little clearer to me now.

-Andy



Ryan B. wrote:
> On Apr 7, 2006, at 9:07 AM, Andrew C. wrote:
>
>> That is, does rails know that because games has player1_id and
>> player2_id that games.player1 and games.player2 are objects from the
>> players table?
>>
>> You can see I am confused! ;)
>>
>> -Andy
>
>
> I believe what you actually need is a has_one relationship. Although
> the game does have many players, the way the table is set up, it only
> has one player1 and one player2. Try this:
>
> class Game < ActiveRecord::Base
>    has_one :player1, :class_name => 'Player'
>    has_one :player2, :class_name => 'Player'
> end
>
> You need to specify the class name because otherwise it will look for
> a class called Player1 which of course doesn't exist. You should then
> be able to use game.player1 or game.player2 to access the players.
>
> However, you may find this database design will cause problems later.
> If, for example, you want to support more than two players, you will
> need to add more columns for player3, player4, etc. Then what if you
> want to support, say 16 players. That is a lot of columns and could
> get messy! If this is at all a possibility, you should go with a true
> one-to-many relationship.
>
> You would do this by putting a game_id column in the players table
> and removing the player1 and player2 columns from the games table.
> You could then use the "has_many :players" relationship method in
> your Game model. You can then grab the players with game.players[0]
> or game.players[1], etc. This also allows you to iterate through all
> players instead of having to refer to each one individually.
>
> Ryan
Ryan B. (Guest)
on 2006-04-08 01:29
(Received via mailing list)
On Apr 7, 2006, at 12:40 PM, Andrew C. wrote:
> I setup the model as suggested, identically and still have player1_id
> and player2_id defined the players table, but now it seems
> game.player1
> and game.player2 always refer to the same object, sort of like player2
> is an alias of player1. I suspect has_one is causing this
> relationship...

Sorry about that, I should have used "belongs_to" instead of
"has_one" in my last e-mail. I tried this and it works:

class Game < ActiveRecord::Base
   belongs_to :player1, :class_name => 'Player', :foreign_key =>
'player1_id'
   belongs_to :player2, :class_name => 'Player', :foreign_key =>
'player2_id'
end

The problem was "has_one" uses the game_id column in the players
table to do the matching. The players.game_id column is actually
unnecessary if you are doing it this way since it will use the
player1_id and player2_id columns to do the matching.

Ryan
This topic is locked and can not be replied to.