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.new
或 BoilerplateOriginal.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.
我想在 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.new
或 BoilerplateOriginal.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 overwritingBase.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 withtype = “Firm”
. You can then fetch this row again usingCompany.where(name: '37signals').first
and it will return aFirm
object.