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 false, :force => true do |t|
5. t.integer :user_id
6. t.integer :language_id
7. t.timestamps
8. end
9. end
10.
11. def self.down
12. drop_table :languages_users
13. end
14. end
15.
1.
2. class Languages < ActiveRecord::Migration
3.
4. def self.up
5. create_table "languages", :force => true do |t|
6. t.string "name"
7. t.string "english_name"
8. t.integer "is_default", :default =>
9. end
10. end
11.
12. def self.down
13. drop_table "languages"
14. drop_table "users_languages"
15. end
16. end
17.
1.
2. class Users < ActiveRecord::Migration
3.
4. def self.up
5. create_table "users", :force => true do |t|
6. t.string "login"
7. # other fields excluded for brevity
8. end
9. end
10.
11. def self.down
12. drop_table "users"
13. end
14. end
15.
Here are my models:
user.rb
1.
2. class User < ActiveRecord::Base
3. has_and_belongs_to_many :languages
4. end
5.
language.rb:
1.
2. class Language < ActiveRecord::Base
3. has_and_belongs_to_many :users
4. end
5.
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.
1.
2. def create
3. @user = User.new(params[:user])
4. @user.save
5. end
6.
7. def update
8. params[:user][:language_ids] ||= []
9.
10. @user = User.find(current_user)
11.
12. if @user.update_attributes params[:user]
13. flash[:notice] = "Settings have been saved."
14. redirect_to edit_user_url(@user)
15. else
16. flash.now[:error] = @user.errors
17. setup_form_values
18. respond_to do |format|
19. format.html { render :action => :edit}
20. end
21. end
22.
23. end
24.
On to the view:
1.
2.
3.
4.
5.
6.
7.
NOTE: I had an error in my original method. This code:
1.
2. user_speaks_language?(language)}, "#{language.id}", "" -%>
3.
should be this:
1.
2.
3.
And we'll need this helper method:
1.
2. def user_speaks_language?(language)
3. if @user && !@user.login.nil? # no sense in testing new users that have no languages
4. @user.languages.include?(language)
5. else
6. false
7. end
8. end
9.
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
Comments
blog comments powered by Disqus