Rails 5 – 对象关系阻碍以及如何构造多个inheritance的类/表

编辑我已经从原版编辑了这个,以便更容易理解。


我理解对象关系阻抗问题。 我理解Rails STI和多态(Rails方式,它不是真正的OO多态)。 我已经阅读了一大堆关于此的博客和问题,但仍无法找到这个问题的答案。

class Person < ApplicationRecord (ie what was ActiveRecord::Base) end class Employee < Person end class Customer < Person end 

……多种其他类型的人

现在让我们说’客户’要求扩展系统,并创建一些新东西。 我们称之为项目,我们可以为其分配员工:

好的,让我们使用第三范式创建多对多:

 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 

这不行。 迁移将失败,因为没有名为Employee的表来创建外键约束。 STI意味着’基类’是People表。

两个问题:

1你是如何解决这个问题的? (为此你可能还想在这里筹码)

2如何为项目(应包括员工,但不包括人员或人员的其他子类型)正确创建序列化数据?

 ProjectSerializer < ActiveModelSerializers has_many :employees has_many :employees, through: :assignments end 

不会工作,所以你必须序列化人。


UPDATE


在迁移中,我创建了Project和Assignment表(Person已存在)。

所以现在数据库有这些表:

 Project Person Assignment 

赋值有两个外键(引用Person,因为那是存在的表,而不是Employee):

 person_id project_id 

每次我尝试创建一个赋值时,都会抛出此错误,我当然希望这样:

 ActiveModel::UnknownAttributeError (unknown attribute 'employee_id' for Assignment.) 

根据Rails文档( 第4.1.2.5节 )的解决方案以及我可以在任何地方找到的这种情况的其他答案是告诉Rails foreign_key是什么。 像这样:

 class Assignment < ApplicationRecord belongs_to :employee, foreign_key: "person_id" belongs_to :project end 

但是我发现的每个例子(甚至在文档中) 都假设没有inheritance – 所有模型都inheritance自ActiveRecord:Base(或Rails 5中的ApplicationRecord)。

即使我明确地告诉Rails,赋值表有一个名为’person_id’的foreign_key,它持有员工的id,但它仍然无法找到它。

最后我试了一下(感谢@camonz的回答)

 class Assignment < ApplicationRecord belongs_to :person, foreign_key: "person_id", foreign_type: "employee" belongs_to :project end 

同样的错误。

这真的是Rails无法处理的模型设置吗?

这是Rails日志:

 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' 

  1. 在迁移时,将FK约束放在addressses表上。 在子类上重新定义has_many关系并指定:foreign_key:foreign_type
  2. 在Assignment序列化程序中,指定belongs_to :employee ,AMS应该正确处理它。

    另请参阅has_many关联的:source:source_type选项。

我找到了第一个问题的解决方案。 将模型设置为问题中的模型,我更改数据库以查看它是否可行。

我更改了Assignment表,以便foreign_key现在称为’employee_id’,因为Rails似乎想要坚持这一点。

然后我改变了约束,现在读取:

 ALTER TABLE public.assignments ADD CONSTRAINT fk_rails_52f37556f9 FOREIGN KEY (employee_id) REFERENCES public.people (id) MATCH SIMPLE ON UPDATE NO ACTION ON DELETE NO ACTION; 

然后代码可以工作 – 但是我现在必须编写一个迁移脚本来创建这样的约束。

原始迁移不起作用,因为它创建’person_id’:

  t.references :person, foreign_key: true 

这个答案清楚地表明Rails没有正确支持外键。

此解决方案的另一个问题是,如果将来某些Person其他子类型也需要分配给Projects ,则他们不能。 所以不是一个很好的解决方案。 最好留下直接与Person相关的作业。

我会回答你的第一个问题,因为对我来说是主要问题。 对我来说,当它要求有关移民的员工表时,有一个线索。

这不行。 迁移将失败,因为没有名为Employee的表来创建外键约束。 STI意味着’基类’是People表。

这将告诉我你在people表上的模式缺少“type”列名,它将存储Object的类名并触发Rails的STI行为。

STI意味着’基类’是People表。

排序但你的错误表明所发生的一切都是正常的rubyinheritance。 所以你基本上有在STI而不是ST部分。

STI的重点不仅仅是父类的inheritance,这是正常的rubyinheritance,重点是在数据库中使用单个表的多个类,因为它们本质上是相同的模式。 但是,它需要看似不可见的“类型”列名来触发数据库中STI的Rails约定。 没有它,它的正常rubyinheritance。

注意:过去我走过这条路,我会警告你,这种方式是疯狂的。 特别是当表格是自我引用时,因为你要在这里提到外键,即同一个表的外键。

当你进入第2步时要小心,因为你设置了无限的引用可能性(竞争条件)或者至少在大O查找时如果你开始过于急切地调用关联。 例如,JSON可以为每个Person构建员工关联。 因此,它必须多次调用同一个表“人员”来构建每个员工的员工,即它成为员工。 这会很慢。