Rails 单一 table 继承:类型列不会自动填充

Rails single table inheritance: type coumn is not populated automatically

我想在 Rails 4 中使用 STI。我已经有一个模型 Boilerplate,现在我想从中继承一个 BoilerplateOriginal 和一个 BoilerplateCopy 模型.所以我在 table:

中添加了一列 type
class AddTypeToBoilerplates < ActiveRecord::Migration
  def up
    add_column :boilerplates, :type, :string
    Boilerplate.update_all type: 'BoilerplateOriginal'
    change_column :boilerplates, :type, :string, null: false
  end

  def down
    remove_column :boilerplates, :type
  end
end

遗憾的是,Rails 似乎没有自动填写此栏:

[1] a4aa2 »  x = Boilerplate.new
=> #<Boilerplate:0x00000101609580> {
               :id => nil,
            :title => nil,
             :type => nil
}
[2] a4aa2 »  x.valid?
  Boilerplate Exists (0.4ms)  SELECT  1 AS one FROM "boilerplates" WHERE "boilerplates"."title" IS NULL LIMIT 1
=> false
[3] a4aa2 »  x.errors
=> {
  :title => [
    [0] "must not be empty"
  ]
}
[4] a4aa2 »  x.title = 'test'
=> "test"
[5] a4aa2 »  x.valid?
  Boilerplate Exists (0.1ms)  SELECT  1 AS one FROM "boilerplates" WHERE "boilerplates"."title" = 'test' LIMIT 1
=> true
[6] a4aa2 »  x.save
   (0.1ms)  begin transaction
  Boilerplate Exists (0.2ms)  SELECT  1 AS one FROM "boilerplates" WHERE "boilerplates"."title" = 'test' LIMIT 1
  SQL (1.4ms)  INSERT INTO "boilerplates" ("title") VALUES (?)  [["title", "test"]]
SQLite3::ConstraintException: NOT NULL constraint failed: boilerplates.type: INSERT INTO "boilerplates" ("title") VALUES (?)
   (0.1ms)  rollback transaction
from /Users/josh/.rvm/gems/ruby-2.1.0@a4aa2/gems/sqlite3-1.3.10/lib/sqlite3/statement.rb:108:in `step'

我是不是漏掉了什么重要的东西?我以为 Rails 只是在 :type 列可用时填充它?

嗯。你做错了of-course。只有当您使用 parent Boilerplate 的任何 child class 创建 objects 时,才会设置 type 列.但是如果使用Boilerplate.new,则需要手动传递type值。另一方面,当您执行 BoilerplateCopy.newBoilerplateOriginal.new 时,ActiveRecord 默认会为您设置 type 为 child 的 class 名称] class.

阅读Single table inheritance的官方文档。

Active Record allows inheritance by storing the name of the class in a column that by default is named type (can be changed by overwriting Base.inheritance_column). This means that an inheritance looking like this:

class Company < ActiveRecord::Base; end
class Firm < Company; end
class Client < Company; end
class PriorityClient < Client; end

When you do Firm.create(name: "37signals"), this record will be saved in the companies table with type = “Firm”. You can then fetch this row again using Company.where(name: '37signals').first and it will return a Firm object.