Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

has_many :through questions

I was previously using has_and_belongs_to_many, and have converted to has_many :through. Here's how it looks for a list of games that can have many users playing. With this, I can do game.users and user.games....:

class Game < ActiveRecord::Base
 has_many :game_users, :dependent => :destroy
 has_many :users, :through => :game_users, :uniq => true
end

class User < ActiveRecord::Base
 has_many :game_users, :dependent => :destroy
 has_many :games, :through => :game_users, :uniq => true
end

class GameUser < ActiveRecord::Base
  belongs_to :game
  belongs_to :user
end

And my database migration for the join table:

create_table :game_users, :id => false do |t|
      t.column :game_id, :integer
      t.column :user_id, :integer
      t.column :player_index, :integer
    end

I'm not quite sure I get all this, please help me check my facts:

  1. Is the dependent => :destroy correct? I want the 'game_users' join table entry to be deleted if either the game or the user is destroyed - but I don't want users to be deleted if games are deleted and vice versa..... ?

  2. The uniq field is supposed to say that games contain only unique users, and the users only contain unique games. Is that correct?

  3. The database migration as before has :id => false. Is that still the right thing to do? I tried in the console destroying a game, and got complaints about the missing id... so I'm guessing not and trying to understand why.

I find rails active record associations very confusing. I guess they aren't supposed to be!

like image 917
cmaughan Avatar asked Nov 10 '09 20:11

cmaughan


1 Answers

1: Yes, that's correct

2: From the documentation on uniq:

If true, duplicates will be omitted from the collection. Useful in conjunction with :through.

So, yes, if you aim is not get the same game in User's games-collection nor the same user in Game's users-collection, that's correct. It's all explained here.

It will not, however, prevent duplicate GameUsers from being created. For that, you'd need to use validates_ uniqueness _of in the GameUser-model:

class GameUser < ActiveRecord::Base
  validates_uniqueness_of :game_id, :scope => :user_id
end

3: No, you don't want to use :id => false any more. By switching from has_and_belongs_to_many to has_many :through, you have promoted your many-to-many join-table to a full model - GameUser - which requires its own id.

While it is old, this is still a good article for understanding has_many :through.

like image 185
Casper Fabricius Avatar answered Oct 25 '22 05:10

Casper Fabricius