Checkbox list in Ruby on Rails using HABTM
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
More from jbasdf
- A copy of ApplicationController has been removed from the module tree but is still active! And can’t dup NilClass
- Ajax calls using jQuery in Ruby on Rails
- Implicit multipart emails using Ruby on Rails and ActionMailer won’t work
- How New Carpet and Rattlesnake turned me into a Consultant or What the Hell Happened?
- 2008-08-01-04-04-01
-
Vermin
-
Justin Ball
-
Frank
-
Vermin
-
Frank
-
Justin Ball
-
Justin Ball
-
Joshua Lippiner
-
jjinux
-
jbasdf
-
jjinux
-
jbasdf
-
mkrx
-
jbasdf
-
jjinux
-
mkrx
-
jjinux
-
jjinux
-
akellakarthik
-
jjinux
-
arun
Justin Ball is a software consultant and entrepreneur with a passion for Ruby. He evolved from a C++ and .Net monkey into a python programmer and finally found Ruby. In the rare moments when he isn't writing code, talking about code or measuring his code productivity in profanity per hour, you can find him on his bike in the mountains or on the roads surrounding Cache Valley. 









