Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rails 3.2 - accepts_nested_attributes_for and join models

I have the following models: user, role, user_role (user_role is a join model)

I am trying to edit a user's roles using checkboxes on the user#edit page. Here's my attempt, I feel like I'm missing something significant, or taking the wrong approach.

user.rb

has_many :user_roles, dependent: :destroy
has_many :roles, through: :user_roles

attr_accessible :user_roles_attributes

accepts_nested_attributes_for :user_roles, reject_if: lambda { |a| a[:role_id] == 0 }, allow_destroy: true

def has_role?(role_sym)
  roles.any? { |r| r.name.underscore.to_sym == role_sym.downcase }
end

def setup_roles!
  Role.all.each { |role|
    user_roles.build(user_id: id, role_id: role.id) unless has_role?(role.name.to_sym)
  }
end

user_role.rb

belongs_to :user
belongs_to :role
delegate :name, to: :role

role.rb

has_many :user_roles
has_many :users, through: :user_role

users_controller.rb

def edit
  @user = User.find(params[:id])
  @user.setup_roles!
end

def update
  @user = User.find(params[:id])
  if @user.update_attributes(params[:user])
    flash[:notice] = 'User was successfully updated.'
    redirect_to edit_user_path(@user)
  else
    render :edit
  end
end

users/edit.html.haml

= form_for @user do |f|
  = f.fields_for(:user_roles) do |role_form|
    = role_form.check_box :role_id, {}, role_form.object.role_id, 0
    = role_form.hidden_field :user_id
    = role_form.label :name, role_form.object.name

  = f.submit 'Update'
like image 658
ardavis Avatar asked Jan 14 '12 06:01

ardavis


People also ask

What is the use of nested attributes in update_only?

However if the :update_only option is true, the nested attributes are used to update the record’s attributes always, regardless of whether the :id is present. The option is ignored for collection associations.

How do I use nested attributes in one-to-one associations?

For a one-to-one association, this option allows you to specify how nested attributes are going to be used when an associated record already exists. In general, an existing record may either be updated with the new set of attribute values or be replaced by a wholly new record containing those values.

How to limit the number of Associated Records with nested attributes?

Allows you to specify the maximum number of associated records that can be processed with the nested attributes. Limit also can be specified as a Proc or a Symbol pointing to a method that should return a number.

What happens if the size of the nested attributes array exceeds?

If the size of the nested attributes array exceeds the specified limit, NestedAttributes::TooManyRecords exception is raised. If omitted, any number of associations can be processed. Note that the :limit option is only applicable to one-to-many associations.


1 Answers

Here is my solution. I received a lot of help from This Post at RubySource. The way the checkbox is setup, it will destroy a UserRole if "unchecked", and only create it when it is "checked" (why the '0', '1' is on that line.)

users_controller.rb

def edit
  @user = User.find(params[:id])
  @user.setup_roles!
end

user.rb

def has_role?(role_sym)
  roles.any? { |r| r.name.underscore.to_sym == role_sym.downcase }
end

def setup_roles!
  Role.all.each { |role|
    user_roles.build(role: role) unless has_role?(role.name.to_sym)
  }
end

users/edit.html.haml

= form_for @user do |f|                                                            
  = f.fields_for :user_roles do |builder|                                          
    = builder.check_box :_destroy, { checked: builder.object.persisted? }, '0', '1'
    = builder.label :_destroy, builder.object.role.name                            
    = builder.hidden_field :role_id                                                

  = f.submit 'Update'                                                              
like image 149
ardavis Avatar answered Oct 10 '22 00:10

ardavis