【问题标题】:Rails: Is this a use case for Single Table Inheritance (STI)?Rails:这是单表继承(STI)的用例吗?
【发布时间】:2015-12-01 15:26:42
【问题描述】:

考虑这种设置。请理解我们的设置要详细得多,但这是一个简单的示例。

competition 具有name。这是一年一度的比赛。
competition_instances 其中有locationstarts_at
每个比赛都有sports,其中有name

示例:
competition.name:“超级碗”每年都有不同的competition_instances,但sport 保持不变。
相反,competition.name:“Olympics”在每个competition_instance 中都有不同的competition_instances 和不同的sports

最好用competition_instance_sports 作为competition_sports 的子类来创建competition_sportscompetition_instance_sports

目标:如果存在competition_instance_sports 记录,则使用它们,否则使用competition_sports 记录。在我们的真实世界应用程序中,每个比赛/比赛实例可以有 20-50 条运动记录。我们如何才能最好地实现?

【问题讨论】:

    标签: sql ruby-on-rails database-design database-normalization sti


    【解决方案1】:

    根据我从问题中了解到的情况,我看不出 STI 在这种情况下会在哪些方面有所帮助。但是,连接表会带您到达您想要的位置。

    我建议创建一个新表sports,这个模型将包含每项运动的所有具体细节。 competition_instance.rb 将有一个/多个 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- 您将能够在比赛实例表中为每个比赛实例设置特定属性(例如 event_start_timeevent_end_time)。

    【讨论】:

      【解决方案2】:

      我只是想到了一些标准项目,这些项目总是在奥运会上,有些是附加在奥运会上的,比如主办国提出的。

      我会在"reverse manner" 中使用Polymorphic Associations

      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
      

      【讨论】:

        【解决方案3】:

        针对“这是否是 STI 的用例”的原始问题,如果不了解您的环境的全部复杂性,就很难说这是否是。但这里有一些事情需要考虑:

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

        在处理共享许多相同功能和数据字段的模型类时,应考虑 STI,但您需要更精细的控制过度扩展或单独添加到每个类。 STI 允许您在编写专门的功能时将数据保存在单个表中,而不是重复代码或放弃单个功能。

        您已经简化了示例,但听起来 competitioncompetition_instance 本质上不是同一个对象,只是行为差异很小。根据您的描述,我可能会将这些对象重命名为 eventcompetition,或者在说明这些对象实际代表的内容方面对您更有意义的名称。我认为“奥运会”是一项通用项目,而“100m Dash”则是奥运会上的一项比赛。或者“超级碗”在 2014 年只举办了一场比赛,“超级碗 XLIX”。

        您还用database-normalization 标记了这个问题,这对STI 没有帮助。如果您的共享对象之间的属性略有不同,那么您最终会在各处出现空字段。

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

        【讨论】:

          【解决方案4】:

          这不是单表继承的情况,因为competition_instance 不能替代competition 并且1 个competition 可以有多个competition_instances。所以你有 3 张桌子:

          competitions
          sports
          competition_instances
          

          competition_instances 有一个指向 competitions 的外键,因为 1 个 competition 可以有多个 competition_instances,但每个 competition_instance 都只有一个 competition

          您是将sports 附加到competitions 还是competition_instances 取决于您的用例的具体限制。我不完全知道“每个competition/competition_instance 可以有20-50 个sport 记录”是什么意思。我希望每个competition_instance 都恰好有一个sport,因此您可以保留它,或者您也可以将sports 的集合附加到competition,以便您可以检索新的competitionssport 之前有一个competition_instance。我需要有关您的用例的更多详细信息,以便为您提供进一步的建议。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多