使用 STI 的多类别模型与单类别模型? - Rails
Multiple Category Models vs Single Category Model with STI? - Rails
我有四个模型:
- Post
- 产品
- 文章
- 位置
其中每一个都可以有多个类别。唯一的逻辑规则是每个 Class 的类别完全独立,因此 Post 不能有 ArticleCategory,Location 不能有 ProductCatgory 等等。
选项 1:多类别模型
型号:
- Post类别
- 产品类别
- 文章类别
- 位置类别
has_many 至:
的模型
- Post分类
- 产品分类
- 文章分类
- 位置分类
这可行,但它不符合 DRY(不要重复自己)理念。那么,使用 STI 怎么样?
选项 2:具有 STI 的单一类别模型
型号:
- 类别
- 分类
子模型:
- Post类别<<类别
- 产品类别 << 类别
- 文章类别<<类别
- 位置类别 << 类别
这看起来不错,但我不知道如果列相同且逻辑相同,我是否还需要使用 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
:
关联您的 categorizations
和 categories
# == 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
我有四个模型:
- Post
- 产品
- 文章
- 位置
其中每一个都可以有多个类别。唯一的逻辑规则是每个 Class 的类别完全独立,因此 Post 不能有 ArticleCategory,Location 不能有 ProductCatgory 等等。
选项 1:多类别模型
型号:
- Post类别
- 产品类别
- 文章类别
- 位置类别
has_many 至:
的模型- Post分类
- 产品分类
- 文章分类
- 位置分类
这可行,但它不符合 DRY(不要重复自己)理念。那么,使用 STI 怎么样?
选项 2:具有 STI 的单一类别模型
型号:
- 类别
- 分类
子模型:
- Post类别<<类别
- 产品类别 << 类别
- 文章类别<<类别
- 位置类别 << 类别
这看起来不错,但我不知道如果列相同且逻辑相同,我是否还需要使用 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
:
categorizations
和 categories
# == 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