我应该在模型中使用`has_and_belongs_to_many`或`has_many:through`吗?

时间:2013-06-10 00:19:06

标签: ruby-on-rails associations

我已经阅读了Rails网站上的Choosing Between has_many :through and has_and_belongs_to_many,但是我有点困惑,因为我对网站上提供的案例有所不同。

我有两个模型:PropCharacterCostume,角色的服装可以有多个与之关联的道具,但道具不属于该角色,任何数字都可以使用场景中的角色也是如此。

现在我的has_and_belongs_to_many :props模型中有CharacterCostume,它完全符合我的要求:它使用名为character_costumes_props的表格获取与服装相关的所有道具我打电话给CharacterCostume#props

然而,由于“属于很多”部分,协会名称让我失望。服装属于任何道具,因此has_and_belongs_to_many :character_costumes模型中没有Prop

我知道没有它就可以正常运行,但它让我想到也许我应该使用has_many :through关联,但这需要我创建一个多余的模型(它是多余的,对吧?)模型看起来像这样:

class CharacterCostumeProp < ActiveRecord::Base
  belongs_to :character_costume
  has_one :prop
end

此外,has_one代替belongs_to会在这里工作吗?

我希望代码尽可能保持语义,但我不确定这是否会增加资源需求或以某种方式降低性能,因为有一个中间模型。

这两种方法都有某些怪癖/好处吗?我的足够好吗?或者我的想法完全错误,我需要做什么?

谢谢!

3 个答案:

答案 0 :(得分:2)

在HABTM和has_many :through之间做出决定时,您必须问自己的一个最重要的问题是:

我是否要存储特定于该关联的任何信息?

示例1:杂志订阅

读者和杂志之间的多对多关系可能被设想为HABTM或has_many :through。但是,后者在这种情况下更有意义,因为我们可以很容易地想到我们可能想要存储的关联特定信息。

读者通过订阅与杂志相关,每个订阅都可以通过价格,开始日期,发行频率以及是否有效等字段来描述。

示例2:标签

现有Tag模型与Article模型之间的关系显然属于多对多模式。一个特定标签与任何特定文章相关联的事实必然不会影响相同标签是否能够在未来与其他文章类似地关联。

但是,与前面的示例不同,这里关联本身就是我们需要的所有信息。我们只需要知道哪些标签与任何给定的文章相关联。协会何时成立并不重要。它持续多久并不重要。

对我们来说,与标签相关联的文章数量可能很重要。但是该信息存储在Tag模型中,因为它不是特定于关联的。甚至还有一个Rails功能可以处理名为counter_cache

的功能

答案 1 :(得分:2)

我认为你想使用:has_many, :through因为你想要直接使用关系模型 - 什么场景,消耗或损坏等等。

但是,它让你觉得有趣的原因是,has_manybelongs_to在很大程度上并不代表它们在英语中的含义。他们真正的含义分别是“他们有外键”和“我有外键”;例外是:dependent => :destroy行为。

这仍然对has_and_belongs_to_many没有帮助,因为你当时说,“他们有外键,我有外键` - 除了你可以想到它有点像添加一个新的对于“I”和“他们”而言,它们恰好是每个人的相同部分,并且具有这些键。

这有帮助吗?

答案 2 :(得分:0)

  1. has_one不起作用,您需要belongs_to
  2. 如果您的关联模型中有逻辑,则不是多余的。
  3. has_and_belongs_to_many对你来说已经够好了
  4. 见下面的例子

    class Student
      has_and_belongs_to_many :courses
      has_many :teachers, through: :courses
    end
    
    class Teacher
      has_many :courses
      has_many :students, through: :courses
    end
    
    class Course
      has_and_belongs_to_many :students
      belongs_to  :teacher
    
      def boring?
        teacher.name == 'Boris Boring'
      end
    end
    

    在上面的例子中,我使用了这两个版本。看看课程将如何拥有自己的逻辑?看看CourseStudent的课程怎么可能没有?这就是它归结为什么。好吧,对我而言。只要我无法为我的关联模型提供正确的名称和/或模型不需要额外的逻辑或行为,我就会使用has_many through