(Rails)如何绕过STI空字段?

时间:2014-08-31 15:12:36

标签: ruby-on-rails

假设我创建了一个具有简单特征的模型:

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来说很简单 - 让BatBird继承自WingedThing

class Bat < WingedThing

(额外问题:这在数据库中看起来如何?我是否为每WingedThing和每Bat生成了Bird行?这是我对它的理解,但请如果我错了,请纠正我。)

(根据这个可能错误的理解)但是,如果我想要一个FlightlessBird怎么办? air_speed_velocity将是一个毫无意义的领域,给它带来负担是相当不道德的; FlightlessBird的所有实例都将包含laden的空条目和为相应air_speed_velocity生成的数据库行中的WingedThing。这比我需要的数据更多,并且不适用于可伸缩性,但我也不想完全删除这些字段,因为至少有两个其他模型依赖它们。

tl; dr 我希望能够做到的是BirdBat能够访问两者共有的特征,同时拥有{{ 1}}具有相同父表的某些特征,但不在数据库中生成空字段。

建模这样的关系的最佳方法是什么,以确保我能获得最薄的数据库?

2 个答案:

答案 0 :(得分:2)

首先,对于STI,你不为每个子类创建额外的表 - 你的蝙蝠/鸟表不会被使用。

其次,空列通常非常轻量级。例如,在postgres中,每行上都有一个掩码,表示哪些列为空。位掩码指示存在哪些列,因此添加可空列仅每行增加1位(除了一些精简的细微差别)

你基本上有4个选择:

  • 不使用STI(即每个模型一个表,每个模型具有所有属性)。对于任何共享代码,您仍然可以拥有一个公共基类(必须具有`self.abstract_class = true)

  • 使用STI,某些列未用于某些子类

  • 使用STI,但您的子类与一个表有has_one的关系,该表具有该特定子类的额外列

  • 序列化非共享属性(即就数据库而言,它们都具有相同的属性)

所有这些都有有效的案例 - 它们也有缺点。例如,如果您沿着has_one路线走,您可能会发现,根据使用情况,您对额外的表有很多查询。另一方面,如果你很少使用额外的属性,它很棒。

虽然我真的不会冒出几个空列 - 你很可能不会注意到。

答案 1 :(得分:0)

对于可变字段,您可以使用Rails serialized attributes。如果您想选择它们,可以使用PostgreSQL HStore