Rails has_many through aliasing with source and source_type for multiple types
Asked Answered
D

2

11

So here is a sample class

class Company < ActiveRecord::Base
    has_many :investments
    has_many :vc_firms, through: :investments, source: :investor, source_type: 'VentureFirm'
    has_many :angels, through: :investments, source: :investor, source_type: 'Person'
end

@company.angels and @company.vc_firms works as expected. But how would I have @company.investors that are comprised of both source types? That would work for all polymorphics on the investor column of the Investments table? or perhaps a way of using a scope to merge all source_type?

Investment model looks like this:

class Investment < ActiveRecord::Base
  belongs_to :investor, polymorphic: true
  belongs_to :company

  validates :funding_series, presence: true #, uniqueness: {scope: :company}
  validates :funded_year, presence: true, numericality: true
end

Angels are associated through the Person model

class Person < ActiveRecord::Base
    has_many :investments, as: :investor
end

Relevant financial organization model associations:

class FinancialOrganization < ActiveRecord::Base
    has_many :investments, as: :investor
    has_many :companies, through: :investments
end
Decastere answered 9/7, 2013 at 6:19 Comment(0)
G
15

Previous solution was wrong, I misunderstood one of the relations.

Rails cannot provide you with a has_many method crossing a polymorphic relation. The reason is that the instances are spread out through different tables (because they can belong to different models which might or not be on the same table). So, you must provide the source_type if you cross a belongs_to polymorphic relation.

Having said that, supposing you could use inheritance in the Investor like this:

class Investor < ActiveRecord::Base
  has_many :investments
end

class VcFirm < Investor
end

class Angel < Investor
end

The you would be able to remove the polymorphic option from investments:

class Investment < ActiveRecord::Base
  belongs_to :investor
  belongs_to :company

  .........
end

And you would be able to cross the relation and scope it with conditions:

class Company < ActiveRecord::Base
    has_many :investments
    has_many :investors, through :investments
    has_many :vc_firms, through: :investments, source: :investor, conditions: => { :investors => { :type => 'VcFirm'} }
    has_many :angels, through: :investments, source: :investor, conditions: => { :investors => { :type => 'Angel'} }
end
Gynophore answered 24/7, 2013 at 18:19 Comment(3)
Doesn't work. Here's the error: ActiveRecord::HasManyThroughAssociationPolymorphicSourceError: Cannot have a has_many :through association 'Company#investors' on the polymorphic object 'Investor#investor' without 'source_type'. Try adding 'source_type: "Investor"' to 'has_many :through' definition.Decastere
Hmmm... then I'd have to store people and financial organizations in the same table which doesn't feel right.Decastere
I told you the alternatives you have. You cannot use relations intend to because it can't work. If you just want to retrieve a list of both create a method on Company "def investors; vc_firms + angels; end".Gynophore
G
2

I added a method to the Company class that fetches all investors for the company by joining with the investments table:

class Company < ActiveRecord::Base
  has_many :investments
  has_many :vc_firms, :through => :investments, :source => :investor, :source_type => 'VcFirm'
  has_many :angels, :through => :investments, :source => :investor, :source_type => 'Angel'

  def investors
    Investor.joins(:investments).where(:investments => {:company_id => id})
  end
end

http://www.brentmc79.com/posts/polymorphic-many-to-many-associations-in-rails looked pretty helpful for reading up on :source vs. :source_type.

Hope it helps!

Gynecocracy answered 29/7, 2013 at 22:4 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.