Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does Rails orphan join records for polymorphic through associations?

A proposal document can be split up into many different section types (text, fees, schedule) etc

Here it's modelled using a polymoprhic association on the join table.

class Proposal < ActiveRecord::Base
  has_many :proposal_sections
  has_many :fee_summaries, :through => :proposal_sections, :source => :section, :source_type => 'FeeSummary'
end

class ProposalSection < ActiveRecord::Base
  belongs_to :proposal
  belongs_to :section, :polymorphic => true
end

class FeeSummary < ActiveRecord::Base
  has_many :proposal_sections, :as => :section
  has_many :proposals, :through => :proposal_sections 
end

Whilst #create works fine

summary = @proposal.fee_summaries.create
summary.proposal == @propsal # true

#new doesnt

summary = @proposal.fee_summaries.new
summary.proposal -> nil

Should it be returning nil?

On a regular has_many and belongs_to initialized yet unpersisted records will still return their parent assocation (built in memory).

Why won't this work and is it intended behaviour?

Schema.rb

 create_table "fee_summaries", :force => true do |t|
    t.datetime "created_at", :null => false
    t.datetime "updated_at", :null => false
  end

  create_table "proposal_sections", :force => true do |t|
    t.integer  "section_id"
    t.string   "section_type"
    t.integer  "proposal_id"
    t.datetime "created_at",   :null => false
    t.datetime "updated_at",   :null => false
  end

  create_table "proposals", :force => true do |t|
    t.datetime "created_at", :null => false
    t.datetime "updated_at", :null => false
  end

ruby 2.0 rails 3.2.14

like image 586
robodisco Avatar asked Oct 20 '22 11:10

robodisco


1 Answers

ActiveRecord can't know that proposal.fee_summaries is reverse association from fee_summary.proposal. It's because you can define your own association name, have additional constraints on it, etc. - to automatically derive which associations are reverse of which would be insanely hard, if not impossible. So even for most straightforward cases you need to tell it explicitly via inverse_of option on the association declaration. Here's an example for a simple direct association:

class Proposal < ActiveRecord::Base
  has_many :proposal_sections, :inverse_of => :proposal
end

class ProposalSection < ActiveRecord::Base
  belongs_to :proposal, :inverse_of => :proposal_sections
end

2.0.0-p353 :001 > proposal = Proposal.new
 => #<Proposal id: nil, created_at: nil, updated_at: nil> 
2.0.0-p353 :002 > section = proposal.proposal_sections.new
 => #<ProposalSection id: nil, proposal_id: nil, created_at: nil, updated_at: nil> 
2.0.0-p353 :003 > section.proposal
 => #<Proposal id: nil, created_at: nil, updated_at: nil> 

Unfortunately, inverse_of does not support indirect (through) and polymorphic assocations. So in your case there's no easy way to make it work. The only workaround I see is to persist the records (use create), so AR can just query the relationships by key and return correct results.

Check the docs for more examples and explanations: http://apidock.com/rails/ActiveRecord/Associations/ClassMethods

like image 114
Kombajn zbożowy Avatar answered Oct 28 '22 20:10

Kombajn zbożowy