Association for polymorphic belongs_to of a particular type

I was stuck on this sort of reverse association and in Rails 4.2.1 I finally discovered this. Hopefully this helps someone if they're using a newer version of Rails. Your question was the closest to anything I found in regard to the issue I was having.

belongs_to :volunteer, foreign_key: :subject_id, foreign_type: 'Volunteer'

I have found a hackish way of getting around this issue. I have a similar use case in a project of mine, and I found this to work. In your Note model you can add associations like this:

class Note
  belongs_to :volunteer, 
    ->(note) {where('1 = ?', (note.subject_type == 'Volunteer')},
    :foreign_key => 'subject_id'
end

You will need to add one of these for each model that you wish to attach notes to. To make this process DRYer I would recommend creating a module like so:

 module Notable
   def self.included(other)
     Note.belongs_to(other.to_s.underscore.to_sym, 
       ->(note) {where('1 = ?', note.subject_type == other.to_s)},
       {:foreign_key => :subject_id})
   end
 end

Then include this in your Volunteer and Participation models.

[EDIT]

A slightly better lambda would be:

 ->(note) {(note.subject_type == "Volunteer") ? where('1 = 1') : none}

For some reason replacing the 'where' with 'all' does not seem to work. Also note that 'none' is only available in Rails 4.

[MOAR EDIT]

I'm not running rails 3.2 atm so I can't test, but I think you can achieve a similar result by using a Proc for conditions, something like:

belongs_to :volunteer, :foreign_key => :subject_id, 
  :conditions => Proc.new {['1 = ?', (subject_type == 'Volunteer')]}

Might be worth a shot


I had bump into the similar problem. and I finally ironed out the best and most robust solution by using a self reference association like below.

class Note < ActiveRecord::Base
  # The true polymorphic association
  belongs_to :subject, polymorphic: true

  # The trick to solve this problem
  has_one :self_ref, :class_name => self, :foreign_key => :id

  has_one :volunteer, :through => :self_ref, :source => :subject, :source_type => Volunteer
  has_one :participation, :through => :self_ref, :source => :subject, :source_type => Participation
end

Clean & simple, only tested on Rails 4.1, but I guess it should work for previous versions.