Zedrian
Zedrian

Reputation: 909

saving record to join table Has_many :through

I am new to Rails 3 and having trouble with saving records in the Join table. I have been looking around and trying different examples found on this website and from the documentation or books, but I don't understand why I can't get it to work. I am trying to create Authorization by creating Roles and associate them to users. So far I have been trying to assign roles from the update action in the Users controller without prevail.

I have 3 models: the User.rb, role.rb, and assignment.rb (the join table)

class User < ActiveRecord::Base
  has_many :assignments, :dependent => :destroy
  has_many :roles, :through => :assignments, :foreign_key => :role_id
  accepts_nested_attributes_for :roles
  attr_accessor :password, :role_ids
  attr_accessible :first_name, :last_name, :email, :password, :password_confirmation, :status, :description, :username, :roles_attributes
  ...
end

class Role < ActiveRecord::Base
  has_many :assignments
  has_many :users, :through => :assignments, :foreign_key => :user_id
  accepts_nested_attributes_for :users
  attr_accessible :name
end

class Assignment < ActiveRecord::Base
  belongs_to :user
  belongs_to :role
  accepts_nested_attributes_for :roles
end  

The Users controller in the update action I have the following

class UsersController < ApplicationController
  ...
  def update
    @user = User.find(params[:id])
    if @user.update_attributes(params[:user])
      @user.roles.build
      flash[:success] = "Profile updated"
      redirect_to @user
    else
      @title = "Edit" + " " + @user.username
      render 'edit'
    end
  end
  ...
end

and in the 'edit' view page I intend to have checkboxes to update the User record with an associated role:

EDIT: Changed the "check_box" with "check_box_tag" ... the check boxes appear properly, but the values are not saved.

<%= form_for(@user) do |f| %>
  ...
  <div class="field">
     <%= f.label :roles %><br />
     <%= f.fields_for :role_ids do |r| %>
       <% @roles.each do |role| %>
         <%= check_box_tag "user[roles][]", role.id, @user.roles.include?(role.id) %>   
         <%= role.name %>   
       <% end %>
       <%= hidden_field_tag "user[roles][]", "" %>
     <% end %>
  </div>
<% end %>

With this code I even get an error where 'Roles' have no association.

EDIT: this was corrected with the accepts_nested_attributes_for :role. Thanks!

No association found for name `roles'. Has it been defined yet?

I am really confused where I am doing something wrong. Your help would be much appreciated.

Aurelien

Upvotes: 0

Views: 1905

Answers (2)

Zedrian
Zedrian

Reputation: 909

Finally solved the problems and thought I could share.

The models associations but I did change the attr_accessible:

class User < ActiveRecord::Base
  has_many :assignments, :dependent => :destroy
  has_many :roles, :through => :assignments, :foreign_key => :role_id
  accepts_nested_attributes_for :roles
  attr_accessor :password
  attr_accessible ..., :roles_ids
  ...
end

In the User controller for the edit and update action.

def edit
  @title = "Edit" + " " + @user.username
  @roles = Role.find(:all)
  @user.assignments.build
end

def update
  @user = User.find(params[:id])
  if @user.update_attributes(params[:user])
    flash[:success] = "Profile updated"
    redirect_to @user
  else
    @title = "Edit" + " " + @user.username
    render 'edit'
  end
end

The important part was the view part and assigning the right names for the checkbox tags

<%= form_for(@user) do |f| %>
  <div class="field">
  <%= f.label :roles %><br />
  <%= f.fields_for :role_ids do |r| %>
    <% @roles.each do |role| %>
      <%= check_box_tag "user[role_ids][]", role.id, @user.roles.include?(role) %>  
      <%= role.name %>  
    <% end %>
    <%= hidden_field_tag "user[role_ids][]", @user.id %>
  <% end %>
</div>

The check_box_tag lets the form save an array and gives more control than check_box Then in order to assign the multiple Role ids, the name of the check_box_tag should include user[roles_ids][]. Finally the last parameter of the check_box_tag returns if the User has already the roles and checks the checkboxes if true.

I must admit that the name part of the check_box_tags is really confusing but it works :).

Upvotes: 0

moritz
moritz

Reputation: 25767

You have to use the same name with "accepts_nested_attributes_for" as you used defining the association:

class Assignment < ActiveRecord::Base
  belongs_to :user
  belongs_to :role
  accepts_nested_attributes_for :role
end 

Upvotes: 2

Related Questions