Multiple select list in rails

I have recently been tinkering with ruby on rails again and was banging my head against the wall with a multiple select list for a “has and belongs to many” relationship between models. After a lot of Googling and experimenting I finally got it running. The resulting code is deceptively simple!

This example illustrates the relationship between users and their roles. Each user can have multiple roles, and each role can have multiple users.

The User Model

# models/admin/user.rb
class Admin::User < ActiveRecord::Base
  has_one :division, :class_name => "Admin::Division"
  has_and_belongs_to_many :role, :class_name => "Admin::Role"
  validates_presence_of :first_name, :last_name, :user_name, :password
end

The Role Model

# models/admin/role.rb
class Admin::Role < ActiveRecord::Base
  has_and_belongs_to_many :user, :class_name => "Admin::User"
  validates_presence_of :role
end

The User View (partial)

# views/admin/users/_form.html.erb
<% form_for(@user) do |f| %>
  <%= f.error_messages %>
  <p>
    <%= f.label :first_name %><br />
    <%= f.text_field :first_name %>
  </p>
  <p>
    <%= f.label :last_name %><br />
    <%= f.text_field :last_name %>
  </p>
  <p>
    <%= f.label :user_name %><br />
    <%= f.text_field :user_name %>
  </p>
  <p>
    <%= f.label :password %><br />
    <%= f.password_field :password %>
  </p>
  <p>
    <%= f.label :role %><br />
	<%= collection_select(:admin_user, :role_ids, @roles, :id, :role,
		{ :selected => @user.role_ids },
		{:multiple => true, :role => 'admin_user[role_ids][]' }) %>
  </p>
  <p>
    <%= f.submit 'Save' %>
  </p>
<% end %>

The User Controller

# excerpt from controllers/admin/users_controller.rb
# This is just the update method
#
  def update
    # if the multiple select list is empty,
    # make sure the role_ids array exists
    params[:admin_user][:role_ids] ||= []

    respond_to do |format|
      if @user.update_attributes(params[:admin_user])
        flash[:notice] = 'User was successfully updated.'
        format.html { redirect_to(@user) }
        format.xml  { head :ok }
      else
        format.html { render :action => "edit" }
        format.xml  { render :xml => @user.errors, :status => :unprocessable_entity }
      end
    end
  end

Display Comma Delimited Roles

Once I got the select list working, I wanted a simple way to display a comma delimited list of a user’s roles. I messed about with looping over all roles and just displaying the ones that matched the appropriate values, but it turns out there is a much simpler and elegant way to do this. The following snippet of code leverages the HMABT mapping between the user model and role model to access all the role names associated with a particular user. It then joins them all together delimiting the values with commas.

# excerpt from views/admin/users/index.html.erb
#
<%=h user.role.map(&:role).join(', ') %>

The Admin Name Space

Please note that my example is complicated a little by the fact that the models, views and controllers are all within a “/admin” name space. Here’s an excerpt from my routes.rb file which shows the necessary code to make it work.

# excerpt from config/routes.rb
map.namespace :admin do |admin|
    admin.resources :roles, :users
  end

Additional Resources

The following resources were quite helpful to me in getting this working:

 

1 Response » to “Multiple select list in rails”

  1. Thank you for the helpful article. The “role_ids” bit solved my problem (“Expected Object got String” errors.)

Leave a Reply