Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Cannot modify association 'User#roles' because the source reflection class 'Role' is associated to 'Profile' via :has_many

I have three models, a user, a profile and a role, where I'm trying to attach multiple roles to a user through profile.

# user.rb
class User < ActiveRecord::Base
  has_one :profile, dependent: :destroy
  has_many :roles, through: :profile
  has_many :interests, through: :profile
  has_many :industries, through: :profile
end

# profile.rb
class Profile < ActiveRecord::Base
  has_one :user, dependent: :destroy
  has_many :roles
  has_many :interests
  has_many :industries
end

# role.rb
class Role < ActiveRecord::Base
  has_many :profiles
  has_many :users, :through => :profiles
  has_many :users
end

I'm trying to use the following in edit.html.erb (simple_form_for)

<%= f.association :roles,
                   as: :check_boxes,
                   label_method: :name,
                   value_method: :id,
                   label: 'Roles' %>

With the following params

params.require(:user).permit([:email, :first_name, :last_name, :admin, :password, role_ids: [] ])

My edit/update method:

  # GET /users/1/edit
  def edit
    @user.build_profile if @user.profile.nil?
  end

  def update
    respond_to do |format|
      if @user.update(user_params)
        format.html { redirect_to @user, notice: 'User was successfully updated.' }
        format.json { render :show, status: :ok, location: @user }
      else
        format.html { render :edit }
        format.json { render json: @user.errors, status: :unprocessable_entity }
      end
    end
  end

Gives me the following error message

Cannot modify association 'User#roles' because the source reflection class 'Role' is associated to 'Profile' via :has_many.

I've been looking at the problem for hours, but I can't seem to find the mistake.

like image 571
Ramsy de Vos Avatar asked Oct 15 '25 04:10

Ramsy de Vos


1 Answers

There are several issues with the way you have defined your relations.

has_one places the foreign key in the inverse side of the relation. So if you declare a relation with has_one in both ends ActiveRecord can't load the association. Instead you need to use belongs_to on the side with the foreign key:

# user.rb
class User < ActiveRecord::Base
  has_one :profile, dependent: :destroy
end

# profile.rb
class Profile < ActiveRecord::Base
  belongs_to :user, dependent: :destroy
end

When it comes to users and roles you are declaring has_many on both sides, but not telling ActiveRecord where to store the relation. In a bi-directional many to many relationship you need to use has_many through: with a join model or has_and_belongs_to_many.

Usually when building a role based access system you would want to define the relationship so that a role belongs_to a single user instead of having a single Admin role for example with many users. This allows you to scope roles to resources and makes the logic of adding / removing roles much less complicated (you're adding (or removing) a role to/from a user, not adding or removing a user from a role.)

If you are authenticating / authorizing the User model you don't want to use has_many :roles, through: :profiles - the tri-parte join is going to be much slower and adds needless complexity and indirection.

If you really need to have a "profiles" table then use it for auxiliary info about the user and join it when needed instead.

class User < ActiveRecord::Base
  has_many :roles, 
  has_one :profile, dependent: :destroy
end

# Columns:
# - user_id [integer, index, foreign key]
# - resource_id [integer, index]
# - resource_type [string, index]
class Role < ActiveRecord::Base
  belongs_to :user, dependent: :destroy
  belongs_to :resource, polymorphic: true, 
                        dependent: :destroy
end

# Columns:
# - user_id [integer, index, foreign key]
class Profile < ActiveRecord::Base
  belongs_to :user
  # this is option but can be useful
  has_many :roles, through: :user
end

I would really recommend that you start by reading the official rails guides article on associations.

like image 140
max Avatar answered Oct 17 '25 17:10

max



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!