Checkboxes are one of those things that look easy and should be easy, but they aren't always easy. I needed a solution that could create a checkbox list of languages that a user speaks. So I don't forget here's how to do it:
The migrations are important. You have to be sure to exclude the id parameter when you create languages_users or you will get ' Mysql::Error: #23000Duplicate entry' due to the fact that ActiveRecord will try to store a value in the id field that indicates which model created the entry (User.languages << vs Langauges.users). The other option is the create the id parameter so that the direction is maintained but be sure that it is not created as a primary key.
class LanguagesUsers < ActiveRecord::Migration def self.up create_table :languages_users, :id => false, :force => true do |t| t.integer :user_id t.integer :language_id t.timestamps end end def self.down drop_table :languages_users end end
class Languages < ActiveRecord::Migration def self.up create_table "languages", :force => true do |t| t.string "name" t.string "english_name" t.integer "is_default", :default => 0 end end def self.down drop_table "languages" drop_table "users_languages" end end
class Users < ActiveRecord::Migration def self.up create_table "users", :force => true do |t| t.string "login" # other fields excluded for brevity end end def self.down drop_table "users" end end
Here are my models:
user.rb
class User < ActiveRecord::Base has_and_belongs_to_many :languages end
language.rb:
class Language < ActiveRecord::Base has_and_belongs_to_many :users end
In my user_controller.rb the create and update methods are simple. This is thanks to the fact that you get a language_ids method on the user object because of the HABTM relationship.
def create
@user = User.new(params[:user])
@user.save
end
def update
params[:user][:language_ids] ||= []
@user = User.find(current_user)
if @user.update_attributes params[:user]
flash[:notice] = "Settings have been saved."
redirect_to edit_user_url(@user)
else
flash.now[:error] = @user.errors
setup_form_values
respond_to do |format|
format.html { render :action => :edit}
end
end
end
On to the view:
<ul class="checkbox-list"> <% @languages.each do |language| -%> <li><%= check_box_tag "user[language_ids][]", language.id, user_speaks_language?(language) -%> <%= language.english_name -%></li> <% end -%> </ul>
NOTE: I had an error in my original method. This code:
<li><%= f.check_box :language_ids, {:checked => user_speaks_language?(language)}, "#{language.id}", "" -%> <%= "#{language.english_name}" -%></li>
should be this:
<li><%= check_box_tag "user[language_ids][]", language.id, user_speaks_language?(language) -%> <%= language.english_name -%></li>
And we'll need this helper method:
def user_speaks_language?(language) if @user && !@user.login.nil? # no sense in testing new users that have no languages @user.languages.include?(language) else false end end
The result is that you will get a list of check boxes that update values in the join table that is part of the has_and_belongs_to_many relationship. Rails is very cool
Tags: checkbox list · checkboxes · HABTM · has and belongs to many · Ruby On Rails8 Comments











8 responses so far ↓
It’s not working for me. Everytime i try to update only the first checkbox is added. Seems like my category_ids not act as an array even if I have the HABTM relationship.
If I look at my request, all the values is sent to the server but only the first one is added.
Any ideas what to do?
Check your Rails version. I am on 2.1. Not sure if that makes a difference or not.
@Vermin - did you discover an answer to your problem? I have exactly the same thing. I am running Rails 2.1.0.
I also running 2.1.0 and haven’t found a solution yet. Sounds strange that it works for you but not for us.
I found another implementation that worked like a champ for me: http://media.railscasts.com/videos/017_habtm_checkboxes.mov
Unfortunately it is video, but it is short and more importantly, it worked for me. Slightly different approach but nothing radical.
Thanks for the link. I haven’t had time to look into the issue so I am glad that you found a technique that works.
There was an error in my code. Thanks to Frank for pointing out the link with the proper method.
[...] http://www.justinball.com/2008/07/03/checkbox-list-in-ruby-on-rails-using-habtm/ Read More [...]