使用 STI 的多类别模型与单类别模型? - Rails

Multiple Category Models vs Single Category Model with STI? - Rails

我有四个模型:

其中每一个都可以有多个类别。唯一的逻辑规则是每个 Class 的类别完全独立,因此 Post 不能有 ArticleCategory,Location 不能有 ProductCatgory 等等。

选项 1:多类别模型

型号:

has_many 至:

的模型

这可行,但它不符合 DRY(不要重复自己)理念。那么,使用 STI 怎么样?

选项 2:具有 STI 的单一类别模型

型号:

子模型:

这看起来不错,但我不知道如果列相同且逻辑相同,我是否还需要使用 STI。它们之间的唯一区别是关联。

选项 3:没有 STI 的单一类别模型?

有一个 "category_class" 列会不会更好,然后做这样的事情:

class Post < ApplicationRecord

  has_many :categories, -> { where category_class: "Post" }

end

节省类和sub类的数量,并简化整个解决方案。我以前用过这个,但没有用在具有多态关联的东西上,这个有用吗?

可能是我误会了。但是,在我看来...

您可以使用 enum 来指定每个 Category 记录的分类。类似于:

# == Schema Information
#
# Table name: categories
#
#  id                :integer          not null, primary key
#  name              :string           not null
#  categorizes       :integer          not null
#  created_at        :datetime         not null
#  updated_at        :datetime         not null
#
class Category < ApplicationRecord
  has_many :categorizations
  has_many :categorizeables, through: :categorizations

  enum categorizes: {
    post:         0,
    product:      1,
    article:      2,
    location:     3
  }

  class << self 

    def not_for(categorizeable_type)
      where.not(categorizes: categorizeable_type)
    end

  end

end

然后,您可以使用多态连接模型,Categorization类似于:

# == Schema Information
#
# Table name: categorizations
#
#  id                   :integer          not null, primary key
#  category_id          :integer          not null
#  categorizeable_id    :integer          not null
#  categorizeable_type  :string           not null
#  created_at           :datetime         not null
#  updated_at           :datetime         not null
#
class Categorization < ApplicationRecord
  belongs_to :category
  belongs_to :categorizeable, polymorphic: true
end

然后您可以使用 has :many, through:

关联您的 categorizationscategories
# == Schema Information
#
# Table name: posts
#
#  id                   :integer          not null, primary key
#  created_at           :datetime         not null
#  updated_at           :datetime         not null
#
class Post < ApplicationRecord
  has_many :categorizations, as: :categorizeable
  has_many :categories, through: :categorizations

  validate :correct_categorization

  def correct_categorization
    if categories.not_for(:post).any?
      errors.add(:categorization, "is incorrect")
    end
  end

end

因为你说 "Categories are completely separate for each Class",所以我添加了验证。您可能需要 fiddle 一点,但希望它能让您了解它是如何工作的。

我认为@jvillian 提出了一个很好的建议,在这种情况下使用枚举。但是,我不是特别喜欢验证规则......用户不应该看到不属于他试图分类的对象的类别。在这种情况下,我会创建一个作用域关系,所以我会像这样定义 Post 模型:

class Post < ApplicationRecord
  has_many :categorizations, as: :categorizeable
  has_many :categories, -> { where(categorizes: 0)}, through: :categorizations
end

并通过 has_many 通过关系建立关联:

f.association :categories