Rails ActiveRecord关系、STI和继承

Rails ActiveRecord关系、STI和继承,activerecord,sti,Activerecord,Sti,好吧,我已经好几年没有写任何ruby代码了,我的设计可能不正确。考虑到这一点,我正在编写一个小实用程序,通过REST克隆TargetProcess中的项目实体。目标流程有一个数据模型,该模型允许多种类型的父子关系: project:epic:feature:user_story project:feature:user_story project:user_story 然而,从数据结构的角度来看,所有的实体几乎都是相同的,因此使用STI和使用模型来定义关系和继承似乎是有意义的。我创建了一个新的

好吧,我已经好几年没有写任何ruby代码了,我的设计可能不正确。考虑到这一点,我正在编写一个小实用程序,通过REST克隆TargetProcess中的项目实体。目标流程有一个数据模型,该模型允许多种类型的父子关系:

project:epic:feature:user_story
project:feature:user_story
project:user_story
然而,从数据结构的角度来看,所有的实体几乎都是相同的,因此使用STI和使用模型来定义关系和继承似乎是有意义的。我创建了一个新的Rails应用程序,其中仅包含以下模型,以验证我在尝试将Epic与功能关联时遇到的错误:

ActiveModel::MissingAttributeError: can't write unknown attribute `epic_id`
以下是模型:

class TargetProcessEntity < ActiveRecord::Base
end

class Project < TargetProcessEntity
  has_many :epics
  has_many :features
  has_many :user_stories
end

class Project < TargetProcessEntity
  has_many :epics
  has_many :features
end

class Epic < TargetProcessEntity
  belongs_to :project
  has_many   :features
end

class Feature < TargetProcessEntity
  belongs_to :project
  belongs_to :epic
  has_many   :user_stories
end

class UserStory < TargetProcessEntity
  belongs_to :feature
  belongs_to :project
end
虽然Epic和Feature都有一个project_id,但是Feature的实例没有Epic_id属性;尝试将epic分配给功能会爆炸:

[20] pry(main)> epic = Epic.new
=> #<Epic:0x007fcab6c80590
 id: nil,
 type: "Epic",
 name: nil,
 description: nil,
 source_remote_id: nil,
 numeric_priority: nil,
 owner: nil,
 created_at: nil,
 updated_at: nil,
 cloned_remote_id: nil,
 resource_type: "Epic",
 project_id: nil>
[21] pry(main)> feature = Feature.new
=> #<Feature:0x007fcab6d3ba48
 id: nil,
 type: "Feature",
 name: nil,
 description: nil,
 source_remote_id: nil,
 numeric_priority: nil,
 owner: nil,
 created_at: nil,
 updated_at: nil,
 cloned_remote_id: nil,
 resource_type: "Feature",
 project_id: nil>
[22] pry(main)> epic.save
   (0.1ms)  begin transaction
  SQL (0.3ms)  INSERT INTO "target_process_entities" ("type", "resource_type", "created_at", "updated_at") VALUES (?, ?, ?, ?)  [["type", "Epic"], ["resource_type", "Epic"], ["created_at", "2015-10-02 15:18:13.351578"], ["updated_at", "2015-10-02 15:18:13.351578"]]
   (4.6ms)  commit transaction
=> true
[23] pry(main)> feature.epic = epic
ActiveModel::MissingAttributeError: can't write unknown attribute `epic_id`
from /Users/kcallahan/.rbenv/versions/2.0.0-p647/lib/ruby/gems/2.0.0/gems/activerecord-4.2.4/lib/active_record/attribute.rb:138:in `with_value_from_database'
[24] pry(main)> 
[20]pry(main)>epic=epic.new
=> #
[21]撬(主)>功能=功能。新
=> #
[22]撬动(主)>epic.save
(0.1ms)开始事务处理
SQL(0.3ms)插入“目标流程实体”(“类型”、“资源类型”、“创建时间”、“更新时间”)值(?、、?、?)[“类型”、“Epic”]、[“资源类型”、“Epic”]、[“创建时间”、“2015-10-02 15:18:13.351578”]、[“更新时间”、“2015-10-02 15:18:13.351578”]
(4.6ms)提交事务
=>正确
[23]撬(主)>feature.epic=epic
ActiveModel::MissingAttributeError:无法写入未知属性`epic\u id`
from/Users/kcalahan/.rbenv/versions/2.0.0-p647/lib/ruby/gems/2.0.0/gems/activerecord-4.2.4/lib/active\u record/attribute.rb:138:in`with_value_from_database'
[24]撬杆(主)>

我意识到我很可能是做错了什么,或者是做了一个糟糕的设计决定;任何意见都是非常感谢的,因为我一直没能找到任何关于这方面的东西,我的头已经撞了好几天了

好吧,我差一点就搞错了!我将xxx\u id列添加到target\u process\u entities表中。我假设STI表能够响应关系定义,尽管我对STI的内部工作原理和关系的理解已经过时,充其量也不完整……

我可能错了,但您的功能表似乎是Project和Epic之间多对多关系的联接表

如果是这样的话,你的模型可能是这样的

class Project < TargetProcessEntity
  has_many :features
  has_many :epics, through: :features
end

class Epic < TargetProcessEntity
  has_many :features
  has_many :projects, through: :features
end

class Feature < TargetProcessEntity
  belongs_to :project
  belongs_to :epic
  has_many   :user_stories
end
class项目
如果使用相同的名称,则暗示源


我注意到target\u process\u entities.project\u id可能会令人困惑;这映射到目标流程项目ID,而不是本地项目ID。改变了这一点,分配项目突然中断。再次更改架构,使其在主表上具有所有查找id,并且可以正常工作:/new schema:
create_table“target_process_entities”,force::cascade do | t | t.string“type”,null:false t.string“name”t.text“description”t.integer“source_remote__id”t.float“numeric_priority”t.integer“owner”t.datetime“created_at”,null:false t.datetime“updated_at”,null:false t.integer“cloned_remote_id”t.string“resource_type”t.integer“target_process_project_id”t.integer“project_id”t.integer“epic_id”t.integer“feature_id”t.integer“user_story_id”“结束
有趣;我不是那样想的。一部史诗只能属于一个项目。此外,虽然功能必须属于项目,但如果已经设置了该层次结构,它也可能属于Epic。一对多项目:Epics会取消多对多功能的资格吗?我认为如果你去掉has\u many:projects,through::Features并替换为Epic中的belowns\u to::Project就可以了。你不能通过功能访问史诗,因为它有自己的史诗关系
class Project < TargetProcessEntity
  has_many :features
  has_many :epics, through: :features
end

class Epic < TargetProcessEntity
  has_many :features
  has_many :projects, through: :features
end

class Feature < TargetProcessEntity
  belongs_to :project
  belongs_to :epic
  has_many   :user_stories
end