ProblemYou have two models that have a many-to-many relationship to each other. You want to create a select list in the edit view of one model that allows you to associate with one or more records of the other model. SolutionAs part of your application's authentication system, you have users that can be assigned to one or more roles that define access privileges. The many-to-many relationship between users and roles is set up by the following class definitions: app/models/user.rb: class User < ActiveRecord::Base has_and_belongs_to_many :roles end app/models/role.rb: class Role < ActiveRecord::Base has_and_belongs_to_many :users end In the edit action of the Users controller, add an instance variable named @selected_roles and populate it with all of the Role objects. Define a private method named handle_roles_users to handle updating a User object with associated roles from the params hash. app/controllers/users_controller.rb: class UsersController < ApplicationController def edit @user = User.find(params[:id]) @roles = {} Role.find(:all).collect {|r| @roles[r.name] = r.id } end def update @user = User.find(params[:id]) handle_roles_users if @user.update_attributes(params[:user]) flash[:notice] = 'User was successfully updated.' redirect_to :action => 'show', :id => @user else render :action => 'edit' end end private def handle_roles_users if params['role_ids'] @user.roles.clear roles = params['role_ids'].map { |id| Role.find(id) } @user.roles << roles end end end In the Users edit view, create a multiple option select list using options_for_select to generate the options from the objects in the @roles instance variable. Construct a list of existing role associations and pass it in as the second parameter. app/views/users/edit.rhtml: <h1>Editing user</h1> <% form_tag :action => 'update', :id => @user do %> <%= render :partial => 'form' %> <p> <select name="role_ids[]" multiple="multiple"> <%= options_for_select(@roles, @user.roles.collect {|d| d.id }) %> </select> </p> <%= submit_tag 'Edit' %> <% end %> <%= link_to 'Show', :action => 'show', :id => @user %> | <%= link_to 'Back', :action => 'list' %> To display the roles associated with each user, join them as a comma-separated list in view of the show action: app/views/users/show.rhtml: <% for column in User.content_columns %> <p> <b><%= column.human_name %>:</b> <%=h @user.send(column.name) %> </p> <% end %> <p> <b>Role(s):</b> <%=h @user.roles.collect {|r| r.name}.join(', ') %> </p> <%= link_to 'Edit', :action => 'edit', :id => @user %> | <%= link_to 'Back', :action => 'list' %> DiscussionThere are a number of helpers available for turning collections of objects into select lists in Rails. For example, the select or select_tag methods of ActionView::Helpers::FormOptionsHelper will generate the entire HTML select tag based on a number of options. Most of these helper methods generate only the options list. Figure 5-4 shows two roles selected for a user and how those roles are listed in the view of the show action. Figure 5-4. A form allowing selection of multiple items from a select listSee Also
|