"has_many :through =>" and checkboxes

Posted by Jeff Kupperman Sat, 29 Sep 2007 03:51:24 GMT

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!

Leave a comment, View comments, View trackbacks

Your Comments.

Leave your own response

Spread the word.

Jeff Kupperman supports RSS (Real Simple Syndication), and Trackbacks from other blogs.

RSS feed for this post Trackback URI

Your Reply

Comment Form.

Fields denoted with a "*" are required.

You may also like to leave your email or website.