How can I get the associations from the polymorphic class to the related classes

47 Views Asked by At

I am trying to get some polymorphic associations to work properly but I am unable to get them right. Currently they give me whatever the ID matches for both tables instead of the direct relation.

class User < ApplicationRecord
  belongs_to :origin, polymorphic: true
  has_one :customer, foreign_key: :id, primary_key: :origin_id, class_name: "Customer"
  has_one :employee, foreign_key: :id, primary_key: :origin_id, class_name: "Employee"
end

class Customer < ApplicationRecord
  has_one :user, class_name: "User", foreign_key: :origin_id
end

class Employee < ApplicationRecord
  has_one :user, class_name: "User", foreign_key: :origin_id
end

If I do:

user = User.create(origin: Customer.find(1))
user.customer # => #<Customer:0x000000014a78d2c8>
# I expect that the code below to be nil but it is not
user.employee # => #<Employee:0x000000014a78d2c8>

Does anyone know how can I get the proper association? Thanks in advance.

2

There are 2 best solutions below

2
mechnicov On

In the rails guides there is example with has_many polymorphic associations

has_one has almost the same code, also read more about belongs_to association options

class User < ApplicationRecord
  belongs_to :origin, polymorphic: true

  belongs_to :customer, -> { where(users: { origin_type: 'Customer' }).includes(:user) }, foreign_key: :origin_id
  belongs_to :employee, -> { where(users: { origin_type: 'Employee' }).includes(:user) }, foreign_key: :origin_id

  # Or simply define instance methods if you don't need above associations:
  # def customer
  #   origin if origin_type == 'Customer'
  # end

  # def employee
  #   origin if origin_type == 'Employee'
  # end
end

class Customer < ApplicationRecord
  has_one :user, as: :origin
end

class Employee < ApplicationRecord
  has_one :user, as: :origin
end

In any case you need such schema migration:

create_table :users do |t| # or change_table if it is existing table
  t.belongs_to :origin, polymorphic: true # origin_id and origin_type columns
end

Output should be something like this

user = User.create(origin: Customer.find(1))
user.customer # => #<Customer:0x000000014a78d2c8>
user.employee # => nil
1
surya narayanan On

First there are a couple of things that need to be changed.You don't need to define separate associations for customer and employee as you will be using a polymorphic association and don't need to explicitly specify the primary keys and foreign keys.

So We should change the code as follows:

class User < ApplicationRecord
  belongs_to :origin, polymorphic: true
end

class Customer < ApplicationRecord
  has_one :user, as: :origin
end

class Employee < ApplicationRecord
  has_one :user, as: :origin
end

For learning on how this works, I suggest you to go through official docs on active record associations https://guides.rubyonrails.org/association_basics.html#polymorphic-associations

And for getting the user record from customer or employee you can use customer.origin or employee.origin respectively. if you wish to get the associated origin type of user you can use user.origin.origin_type