假设我创建了一个具有简单特征的模型:
class CreateWingedThings < ActiveRecord::Migration
create_table :winged_things do
t.integer :number_of_wings
t.integer :species
t.integer :air_speed_velocity
t.boolean :laden, :default => false
end
end
但现在我想要两种特定类型的WingedThing
,它们有各自不同的特征:
class CreateBats < ActiveRecord::Migration
create_table :bats do
t.integer :echolocation_volume
t.string :snout_type
end
end
class CreateBirds < ActiveRecord::Migration
create_table :birds do
t.string :beak_size
t.integer :number_of_feathers
end
end
这对STI来说很简单 - 让Bat
和Bird
继承自WingedThing
:
class Bat < WingedThing
(额外问题:这在数据库中看起来如何?我是否为每WingedThing
和每Bat
生成了Bird
行?这是我对它的理解,但请如果我错了,请纠正我。)
(根据这个可能错误的理解)但是,如果我想要一个FlightlessBird
怎么办? air_speed_velocity
将是一个毫无意义的领域,给它带来负担是相当不道德的; FlightlessBird
的所有实例都将包含laden
的空条目和为相应air_speed_velocity
生成的数据库行中的WingedThing
。这比我需要的数据更多,并且不适用于可伸缩性,但我也不想完全删除这些字段,因为至少有两个其他模型依赖它们。
tl; dr 我希望能够做到的是Bird
和Bat
能够访问两者共有的特征,同时拥有{{ 1}}具有相同父表的某些特征,但不在数据库中生成空字段。
建模这样的关系的最佳方法是什么,以确保我能获得最薄的数据库?
答案 0 :(得分:2)
首先,对于STI,你不为每个子类创建额外的表 - 你的蝙蝠/鸟表不会被使用。
其次,空列通常非常轻量级。例如,在postgres中,每行上都有一个掩码,表示哪些列为空。位掩码指示存在哪些列,因此添加可空列仅每行增加1位(除了一些精简的细微差别)
你基本上有4个选择:
不使用STI(即每个模型一个表,每个模型具有所有属性)。对于任何共享代码,您仍然可以拥有一个公共基类(必须具有`self.abstract_class = true)
使用STI,某些列未用于某些子类
使用STI,但您的子类与一个表有has_one
的关系,该表具有该特定子类的额外列
序列化非共享属性(即就数据库而言,它们都具有相同的属性)
所有这些都有有效的案例 - 它们也有缺点。例如,如果您沿着has_one路线走,您可能会发现,根据使用情况,您对额外的表有很多查询。另一方面,如果你很少使用额外的属性,它很棒。
虽然我真的不会冒出几个空列 - 你很可能不会注意到。
答案 1 :(得分:0)
对于可变字段,您可以使用Rails serialized attributes。如果您想选择它们,可以使用PostgreSQL HStore。