EDIT I have edited this from the original to make it easier to understand.
I understand the Object Relationship Impedance problem. I understand Rails STI and Polymorphism (the Rails way and that it's not true OO Polymorphism). I've read a TON of blogs and questions on this, and still cannot find the answer to this problem.
class Person < ApplicationRecord (ie what was ActiveRecord::Base)
end
class Employee < Person
end
class Customer < Person
end
... multiple other types of Person
Now lets say the 'client' asks to extend the system, and create some new stuff. Let's call it a Project, to which we can assign Employees:
Ok so let's create a Many-to-Many using Third Normal Form:
class Project < ApplicationRecord
has_many :assignments
has_many :employees, through: :assignments
end
class Employee < Person
has_many :assignments
has_many :projects, through: :assignments
end
class Assignment < ApplicationRecord
belongs_to :employee
belongs_to :project
end
This won't work. The migration will fail, since there is no table called Employee to create the foreign-key constraints on. STI means that the 'base class' is the People table.
TWO QUESTIONS:
1 How do you solve this? (for this you may also want to chip in here)
2 How do you create serialised data properly for Projects (which should include employees, but not People or other sub-types of Person)?
ProjectSerializer < ActiveModelSerializers
has_many :employees
has_many :employees, through: :assignments
end
won't work, so you would have to serialise People.
UPDATE
In the migration I created Project and Assignment tables (Person already exists).
So now the database has these tables:
Project
Person
Assignment
Assignment has two foreign keys (referencing Person, since that is the table that exists, not Employee):
person_id
project_id
Every time I try to create an assignment, this error is thrown, which I expected of course:
ActiveModel::UnknownAttributeError (unknown attribute 'employee_id' for Assignment.)
The solution according to Rails documentation (section 4.1.2.5) and every other answer I can find anywhere for this situation is to tell Rails what the foreign_key is. Like this:
class Assignment < ApplicationRecord
belongs_to :employee, foreign_key: "person_id"
belongs_to :project
end
But every example I find (even in the documentation) all assume that there is no inheritance - all the models are inheriting from ActiveRecord:Base (or ApplicationRecord in Rails 5).
Even though I am explicitly telling Rails that the assignment table has a foreign_key called 'person_id' that holds the id for the employee, it still cannot find it.
And finally I tried this (thanks to the answer from @camonz)
class Assignment < ApplicationRecord
belongs_to :person, foreign_key: "person_id", foreign_type: "employee"
belongs_to :project
end
Same error.
Is this really a model setup that Rails cannot handle?
Here is the Rails log:
I, [2016-09-22T22:54:55.088466 #12182] INFO -- : Started POST "/assignments" for ::1 at 2016-09-22 22:54:55 +0200
I, [2016-09-22T22:54:55.095768 #12182] INFO -- : Processing by AssignmentsController#create as JSON
I, [2016-09-22T22:54:55.096007 #12182] INFO -- : Parameters: {"data"=>{"attributes"=>{"status"=>"pending"}, "relationships"=>{"project"=>{"data"=>{"type"=>"projects", "id"=>"601"}}, "employee"=>{"data"=>{"type"=>"employees", "id"=>"143"}}}, "type"=>"assignments"}, "assignment"=>{}}
I, [2016-09-22T22:54:55.098032 #12182] INFO -- : {:status=>"pending", :project_id=>"601", :employee_id=>"143"}
I, [2016-09-22T22:54:55.117411 #12182] INFO -- : Completed 500 Internal Server Error in 21ms (ActiveRecord: 8.8ms)
F, [2016-09-22T22:54:55.119116 #12182] FATAL -- :
F, [2016-09-22T22:54:55.119246 #12182] FATAL -- : ActiveModel::UnknownAttributeError (unknown attribute 'employee_id' for Assignment.):
F, [2016-09-22T22:54:55.119283 #12182] FATAL -- :
F, [2016-09-22T22:54:55.119313 #12182] FATAL -- : app/controllers/assignments_controller.rb:18:in `create'