I was working with Jaime on the Fair Labor simulation, and we ran into this problem:
There will be multiple games, each with multiple users, but a user may belong to more than one game. She wanted to use a checkbox list to add and remove users from a game. So far so good -- it's a classic "has_and_belongs_to_many" problem, and the code is very concise. We made a join table, games_models, using the migration in my previous post. Then:
In the Game model:
has_and_belongs_to_many :users
In the User model
has_and_belongs_to_many :games
In the view (edit_game.rhtml):
<% form_tag :controller=>'main', :action=>'update_game', :id=>@game.id do %> <% for user in User.find(:all) %> <%= check_box_tag "game[user_ids][]", user.id, @game.users.include?(user) %> <%= user.name %> <% end %> <P><%= submit_tag 'Add' %></P> <% end %>
In the controller:
def edit_game @game = Game.find(params[:id]) @users = User.find(:all) end def update_game @game = Game.find(params[:id]) @game.update_attributes(params[:game]) redirect_to :action=>'edit_game', :id=>@game end
So we made a join table the usual way and used "has_and_belongs_to_many" in the models, and everything was fine and dandy...except she needed to identify some of the roles as "leader" roles -- in other words, the HABTM relationship needed to contain additional information.
So we made a model called Member to hold the join table, and that was fine, but we no longer could use the magic that comes with HABTM. Instead, we needed to set up the relationships in the models manually, like this:
class Game < ActiveRecord::Base has_many :members, :dependent => true has_many :users, :through => :members end class User < ActiveRecord::Base has_many :members, :dependent => true has_many :games, :through => :members end class Member < ActiveRecord::Base belongs_to :user belongs_to :game end
That worked, sort of, but it turns out that the "game[user_ids][]" nomenclature goes with HABTM, and when we did it manually, it choked. Thankfully, we're not the first people to get stuck on this (see http://railsforum.com/viewtopic.php?id=803). We can write our own user_ids method, as follows, which we put in the Game model declaration (game.rb):
def user_ids=(user_ids)
members.each do |mb|
mb.destroy unless user_ids.include? mb.user_id
end
user_ids.each do |user_id|
self.members.create(:user_id => user_id) unless members.any? { |d| d.user_id == user_id }
end
end
Whew!