Home > database >  how can I do inverse of self-reference with rails
how can I do inverse of self-reference with rails

Time:09-26

I have the user model like :

  has_many :users, class_name: 'User'
  belongs_to :master_user, class_name: 'User', optional: true, inverse_of: :users

I Would like to find :

User.first.master_user it's ok

MasterUser.users but get an error : "NameError: uninitialized constant MasterUser"

CodePudding user response:

You are getting that error because you have not defined a MasterUser model. I am guessing you only have a User model as described in your question. If you want to find the users belonging to a "master_user" then you need to find a "master_user" first, then request its users. It would look something like this:

user_with_a_master = User.where.not(master_user_id: nil).first
master             = user_with_a_master.master_user
master_users       = master.users

CodePudding user response:

Here is an example of how to properly setup a self-referential association with a bit less confusing naming:

class User < ApplicationRecord
  belongs_to :manager
    class_name: 'User', # requied here since it cannot be derided from the name
    optional: true,
    inverse_of: :subordinates
  has_many :subordinates,
    class_name: 'User',  # requied here since it cannot be derided from the name
    foreign_key: :manager_id, # what column on the users table should we join
    inverse_of: :manager
end

"Managers" here are not a separate class. While you could use single table inheritance to that purpose you should probally get the basics figured out first. Even if you did have a MasterUser class you would get NoMethodError since you're calling .users on the class and not an instance of the class - that will never work and is a very common beginner misstake.

Note that this strictly speaking would actually work without the inverse_of: option which is really just used to explicity set the two way binding in memory.

So in your case it should look like:

class User < ApplicationRecord
  # class_name isn't required since it can be derided from the name
  has_many :users, 
    foreign_key: :master_user_id,
    inverse_of: :master_user
  belongs_to :master_user, 
    class_name: 'User', # requied here since it cannot be derided from the name
    optional: true, 
    inverse_of: :users
end

Note that the users table must have a master_user_id column.

  • Related