在 Rails 的单表继承中在哪里写关联?

Posted

技术标签:

【中文标题】在 Rails 的单表继承中在哪里写关联?【英文标题】:Where to write associations in Single Table Inheritance in rails? 【发布时间】:2019-10-14 13:07:10 【问题描述】:

我有一个名为 User 的基类,它有两个名为 Manager 和 Creator 的子类。我已经为 User、Manager 和 Creator 模型实现了单表继承。 还有另外 2 个模型,分别命名为 Project 和 Bug。

class User < ApplicationRecord
end

class Manager < User
end

class Creator < User
end

class Project < ApplicationRecord
end

class Bug < ApplicationRecord
end

我想要的联想是

一个项目可以有多个用户 一个经理可以有多个项目 一个创造者可以创造许多错误

我理解这些关系,但我无法理解将关联放在哪里(对于用户及其子类)。 例如 对于关联:一个 Creator 可以创建许多 Bug

class Creator < User
   has_many :bugs
end

关联声明的另一面应该放在哪里?我是写在 User 类还是 Creator 类中? 即

class Bug < ApplicationRecord
   belongs_to :user
end

class Bug < ApplicationRecord
   belongs_to :creator
end

哪一种方法是正确的? 问题的底线是: 在单表继承中,我是与父模型还是子模型建立关联(其他模型)?

【问题讨论】:

UserManagerCreator 看起来都像是由 Person 担任的角色。我对您的域一无所知,但 STI 不是我的首选方法。 你建议用什么来代替 STI?我应该将所有这些实体作为我的域模型的一部分。 【参考方案1】:

作为对您的评论的回应,下面是一种潜在的方法,可以对您枚举的关联进行建模,仅使用 UserProjectBugProjectUser,而不使用 STI。

模拟A Creator can create many Bugs 的简单方法可能类似于:

class User < ApplicationRecord
  has_many :bugs, foreign_key: :creator_id
end

对于您的Bug 模型:

# == Schema Information
#
# Table name: bugs
#
#  id                     :integer          not null, primary key
#  creator_id             :integer
#  created_at             :datetime         not null
#  updated_at             :datetime         not null
#
class Bug < ApplicationRecord
  belongs_to :creator, class_name: 'User'
end

在这种情况下,关联定义隐含了角色,但您没有明确的Role 模型。所以如果你有一个@user 的实例,你可以这样做:

@user.bugs.create bug_attributes

如果你有@bug,那么你可以这样做:

@bug.creator

...取回创建BugUser

同样,要对A Manager can have many Projects 建模,您可以执行以下操作:

class User < ApplicationRecord
  has_many :bugs, foreign_key: :creator_id
  has_many :managed_projects, class_name: 'Project', foreign_key: :manager_id
end

# == Schema Information
#
# Table name: projects
#
#  id                     :integer          not null, primary key
#  manager_id             :integer
#  created_at             :datetime         not null
#  updated_at             :datetime         not null
#
class Project < ApplicationRecord
  belongs_to :manager, class_name: 'User'
end

同样,角色隐含在关联定义中。而且,使用@user,您可以:

@user.managed_projects.create project_attributes

要对A Project can have many Users 建模(假设是User can belong to many projects),您可以使用类似于以下内容的ProjectUser 模型:

# == Schema Information
#
# Table name: project_users
#
#  id                     :integer          not null, primary key
#  project_id             :integer
#  user_id                :integer
#  created_at             :datetime         not null
#  updated_at             :datetime         not null
#
class ProjectUser < ApplicationRecord
  belongs_to :project
  belongs_to :user
end

然后在你的Project,你可以这样做:

class Project < ApplicationRecord
  belongs_to :manager, class_name: 'User'
  has_many :project_users
  has_many :users, through: :project_users
end

在你的User,你可以这样做:

class User < ApplicationRecord
  has_many :bugs, foreign_key: :creator_id
  has_many :managed_projects, class_name: 'Project', foreign_key: :manager_id
  has_many :project_users
  has_many :projects, through: :project_users
end

现在您可以执行@project.users@user.projects 之类的操作。

根据您的域和用例,您可能希望将 Role 设为显式类。但是,这完全是另一回事。

【讨论】:

以上是关于在 Rails 的单表继承中在哪里写关联?的主要内容,如果未能解决你的问题,请参考以下文章

单表继承 活动管理模型映射问题 rails 4

Rails ActiveRecord_Associations_CollectionProxy 与单表继承

rails中的多态关联

Prisma 中是不是支持单表继承?

通过rails中关联模型的子类查询

单表继承(STI)问题