0
class Sample
  has_many :pictures
end

class Picture < ApplicationRecord
  belongs_to :imageable, polymorphic: true
  belongs_to :sample
end


class Employee < ApplicationRecord
  has_many :pictures, as: :imageable
end

class Product < ApplicationRecord
  has_many :pictures, as: :imageable
end

What should be the association to get all product or employee of a given sample. Sample.first.pictures.map(&:imageable). I want to get it as an activerecord association.

rohan kharvi
  • 118
  • 10

1 Answers1

2

Workaround:

class Sample
  has_many :pictures
  has_many :imageable_employees, through: :pictures, source: :imageable, source_type: 'Employee'
  has_many :imageable_products, through: :pictures, source: :imageable, source_type: 'Product'
end

Usage:

sample = Sample.first
employees = sample.imageable_employees
products = sample.imageable_products

...see docs

Explanation:

Sample.first.pictures.map(&:imageable). I want to get it as an activerecord association.

... is I don't think it's possible, but you can still get them all as an Array instead. The reason is that there is no table (model) that corresponds to the imageable association, but that it corresponds to ANY model instead, which complicates the SQL query, and thus I don't think it's possible.

As an example, consider the following query:

imageables_created_until_yesterday = Sample.first.something_that_returns_all_imageables.where('created_at < ?', Time.zone.now.beginning_of_day)

# what SQL from above should this generate? (without prior knowledge of what tables that the polymorphic association corresponds to)
# =>  SELECT "WHAT_TABLE".* FROM "WHAT_TABLE" WHERE (sample_id = 1 AND created_at < '2018-08-27 00:00:00.000000')

# furthermore, you'll notice that the SQL above only assumes one table, what if the polymorphic association can be at least two models / tables?

Alternative Solution:

Depending on the needs of your application and the "queries" that you are trying to do, you may or may not consider the following which implements an abstract_imageable (a real table) model for you to be able to perform queries on. You may also add more attributes here in this abstract_imageable model that you think are "shared" across all "imageable" records.

Feel free to rename abstract_imageable

class Sample
  has_many :pictures
  has_many :abstract_imageables, through: :pictures
end

class Picture
  belongs_to :sample
  has_many :abstract_imageables
end

# rails generate model abstract_imageable picture:belongs_to imageable:references{polymorphic}
class AbstractImageable
  belongs_to :picture
  belongs_to :imageable, polymorphic: true
end

class Employee < ApplicationRecord
  has_many :abstract_imageables, as: :imageable
  has_many :pictures, through: :abstract_imageables
end

class Product < ApplicationRecord
  has_many :abstract_imageables, as: :imageable
  has_many :pictures, through: :abstract_imageables
end

Usage:

sample = Sample.first
abstract_imageables = sample.abstract_imageables

puts abstract_imageables.first.class
# => AbstractImageable

puts abstract_imageables.first.imageable.class
# => can be either nil, or Employee, or Product, or whatever model

puts abstract_imageables.second.imageable.class
# => can be either nil, or Employee, or Product, or whatever model

# your query here, which I assumed you were trying to do because you said you wanted an `ActiveRecord::Relation` object
abstract_imageables.where(...) 
Jay-Ar Polidario
  • 6,463
  • 14
  • 28