Rails:这是单一 Table 继承 (STI) 的用例吗?

Rails: Is this a use case for Single Table Inheritance (STI)?

考虑一下这个设置。请理解我们的设置更加详细,但这是一个简单的示例。

competition 其中有 name。这是一年一度的比赛。
competition_instances 其中有 location, starts_at.
每个比赛有 sports 其中有 name.

示例:
competition.name: "Super Bowl" 每年都有不同的 competition_instancessport 保持不变。
反之,competition.name:"Olympics"在每个competition_instance中有不同的competition_instances和不同的sports

是否最好创建 competition_sportscompetition_instance_sports 并将 competition_instance_sports 作为 competition_sports 的子类?

GOAL: 如果存在 competition_instance_sports 记录,则使用 competition_sports 记录。在我们真实世界的应用程序中,每个 competition/competition_instance 可以有 20-50 条运动记录。我们怎样才能最好地实现?

根据我对问题的理解,我看不出 STI 在这种情况下有什么帮助。但是加入 table 会让你到达你想要的地方。

我建议创建一个新的 table sports,这个模型将包含每项运动的所有具体细节。 competition_instance.rb 将有 one/many sport.rbcompetiton.rb 将有许多 sportscompetition_instance.rb

competition.rb

Class Competition < ActiveRecord::Base
   has_many :competition_instances
   has_many :sports, through: :competition_instances
end

competition_instance.rb

Class CompetitionInstance < ActiveRecord::Base
   belongs_to :competition
   belongs_to :sport
end

sport.rb

Class Sport < ActiveRecord::Base
   has_many :competition_instances
end

通过使用此设计,您将能够实现以下目标:

1- 您将在数据库中拥有预定义的运动及其特定属性。

2-每场比赛都会有.sports这会给本次比赛的所有运动项目提供奥运案例。

3- 您将能够在比赛实例 table.

中为每个比赛实例(例如 event_start_timeevent_end_time)设置特定属性

这不是单个 table 继承的情况,因为 competition_instance 不能替代 competition 并且 1 个 competition 可以有多个 competition_instances .所以你有 3 tables:

competitions
sports
competition_instances

competition_instances 有一个 competitions 的外键,因为 1 competition 可以有很多 competition_instances 但每个 competition_instance 有一个 competition .

是否将 sports 附加到 competitionscompetition_instances 取决于您的用例的具体限制。我不完全明白你所说的 "each competition/competition_instance can have 20-50 sport records" 是什么意思。我希望每个 competition_instance 恰好有一个 sport,所以您可以保留它,或者您也可以将 sports 的集合附加到 competition,所以您可以在 competition_instance 之前通过 sport 检索新的 competitions。我需要有关您的用例的更多详细信息,以便为您提供进一步的建议。

我只是在考虑这样一种情况,即有标准的项目总是在奥运会上,也有一些是增加的,比如主办国提出的项目。

我会用Polymorphic Associations, in a "reverse manner".

class Competition < ActiveRecord::Base
  has_many :competition_instances
  has_many :competition_sports, as: :event
end

class CompetitionInstance < ActiveRecord::Base
  belongs_to :competition
  has_many :competition_sports, as: :event

  def events_array  # neater by sacrificing ActiveRecord methods
    competition.competition_sports + competition_sports
  end

  def events        # messier, returns ActiveRecord relationship
    CompetitionSport.where( " ( event_id = ? AND event_type = 'Competition' ) OR
                              ( event_id = ? AND event_type = 'CompetitionInstance')", competition_id, id )
  end
end

class Sport < ActiveRecord::Base
  has_many :events, as: :competition_sport
end

class CompetitionSport < ActiveRecord::Base
  belongs_to :sport
  belongs_to :event, polymorphic: true
end

这允许:

competition.competition_sports            # standard sports
competition_instance.competition_sports   # only those specific for this instance
competition_instance.events               # includes sports from both

针对 "Is this a use case for STI" 的原始问题,如果不了解您的环境的全部复杂性,很难说这是不是。但这里有一些需要考虑的事项:

节选自How (and When) to Use Single Table Inheritance in Rails - eugenius blog

STI should be considered when dealing with model classes that share much of the same functionality and data fields, but you want more granular control over extending or adding to each class individually. Rather than duplicate code or forego the individual functionalities, STI permits you to use keep your data in a single table while writing specialized functionality.

你已经简化了你的例子,但听起来 competitioncompetition_instance 本质上不是同一个对象,只有细微的行为差异。根据您的描述,我可能会将这些对象重命名为 eventcompetition,或者在说明这些对象实际代表什么方面对您来说更有意义的名称。我认为 'The Olympics' 是一项通用赛事,而“100 米短跑”是奥运会上举办的一项比赛。或者 "The SuperBowl" 2014 年只举办了一场比赛,"SuperBowl XLIX".

您还用 database-normalization 标记了这个问题,STI 对此无济于事。如果您的共享对象之间的属性略有不同,您最终会到处都是空字段。

我建议您参考此处的其他答案,了解您应该如何设置这些对象以使其按预期运行。