Gleydson S. Tavares
Gleydson S. Tavares

Reputation: 600

Rails has_one with polymorphic Association

I'm having a problem when I try to access relationships or attributes of my models. I have the following code:

class Occupation < ApplicationRecord
  belongs_to :user
  belongs_to :occupationable, polymorphic: true
end

A user can have multiple occupations and these occupations have specific relationships and attributes.

class User < ApplicationRecord
  has_many :occupations, dependent: :destroy
end

class Teacher < ApplicationRecord
  has_one :occupation, as: :occupationable
end

class Psychologist < ApplicationRecord
 has_one :occupation, as: :occupationable
end

class Parent < ApplicationRecord
  has_one :occupation, as: :occupationable
  has_many :children
end

The problem occurs in the Parent classes. A parent can have multiple children, but how can I access these children?

I tried the following but unsuccessfully:

ID | OCCUPATIONABLE_ID | OCCUPATIONABLE_TYPE | USER_ID | CREATED_AT              | UPDATED_AT              
---|-------------------|---------------------|---------|-------------------------|-------------------------
1  | 1                 | Parent              | 1       | 2018-07-26 13:49:06     | 2018-07-26 13:49:06           


User.find(1).occupations.where(occupationable_type: "Parent").children

NoMethodError (undefined method `children' for Occupation::ActiveRecord_AssociationRelation:0x0000559d4c601898

Upvotes: 0

Views: 255

Answers (1)

jvillian
jvillian

Reputation: 20263

User.find(1).occupations.where(occupationable_type: "Parent").children

As indicated by the error, returns an ActiveRecord_AssociationRelation which does not have the method children.

If you do:

User.find(1).occupations.find_by(occupationable_type: "Parent")

That will return a single occupation record. But, you don't want the occupation, you want the Parent. So, try:

User.
  find(1).
  occupations.
  find_by(occupationable_type: "Parent").
  occupationable.
  children

But, if that user doesn't have an occupation with occupation_type "Parent", then you're going to get some errors (because you'll try to call occupationable on a nil object).

So, you could try:

User.
  find(1).
  occupations.
  find_by(occupationable_type: "Parent")&.
  occupationable&.
  children

I don't know where you're using that code. But wherever it is, you need to know a lot about how your models are set up. You might consider going for some encapsulation. Something, perhaps, like:

class User < ApplicationRecord
  has_many :occupations, dependent: :destroy

  def occupation(type)
    occupations.
    find_by(occupationable_type: type)&.
    occupationable
  end

  def children
    occupation('Parent')&.children
  end

end

In which case you could do something like:

User.find(1).children

And now you don't need to know anything about how your models are set up in order to find the children.

Down the road, I imagine you might end up doing something like:

class Teacher < ApplicationRecord
  has_one :occupation, as: :occupationable
  has_many :students
end

In which case, you might do:

class User < ApplicationRecord
  has_many :occupations, dependent: :destroy

  def occupation(type)
    occupations.
    find_by(occupationable_type: type)&.
    occupationable
  end

  def children
    occupation('Parent')&.children
  end

  def students
    occupation('Teacher')&.students
  end

end

You're going to end up with redundancy (maybe a lot). And, it might get tricky when you go to find all the Teachers for a Student or Parents for a Child. But, that's a whole other kettle of fish.

Upvotes: 1

Related Questions