Active Record 知道如何沿着时间线更新数据库模式,使其从任何历史版本更新为最新版本。Active Record 还会更新 db/schema.rb
文件,以匹配最新的数据库结构。
创建独立的迁移
迁移文件储存在 db/migrate
文件夹中,一个迁移文件包含一个迁移类。文件名采用 YYYYMMDDHHMMSS_create_products.rb
形式,即 UTC 时间戳加上下划线再加上迁移的名称。
bin/rails generate migration AddPartNumberToProducts
创建空的迁移,并进行适当命名:
|
|
上面的命令会生成:
|
还可以像下面这样在新建字段上添加索引:
|
上面的命令会生成:
|
如果想在迁移中完成一些 Active Record 不知如何撤销的操作,可以使用 reversible
方法:
|
还可以生成用于添加多个字段的迁移,例如:
|
上面的命令会生成:
|
如果迁移名称是 CreateXXX
的形式,并且后面跟着字段名和类型列表,那么会生成用于创建包含指定字段的 XXX
数据表的迁移。例如:
|
上面的命令会生成:
|
和往常一样,上面的命令生成的代码只是一个起点,我们可以修改 db/migrate/YYYYMMDDHHMMSS_add_details_to_products.rb
文件,根据需要增删代码。
生成器也接受 references
字段类型作为参数(还可使用 belongs_to
),例如:
|
上面的命令会生成:
|
这个迁移会创建 user_id
字段并添加索引外键。
模型生成器
模型和脚手架生成器会生成适用于添加新模型的迁移。这些迁移中已经包含用于创建有关数据表的指令。如果我们告诉 Rails 想要哪些字段,那么添加这些字段所需的语句也会被创建。例如,运行下面的命令:
|
上面的命令会创建下面的迁移:
|
我们可以根据需要添加“字段名称/类型”对,没有数量限制。
创建联结数据表
create_join_table 方法用于创建 HABTM(has and belongs to many)联结数据表。典型的用法像下面这样:
create_join_table :products, :categories
上面的代码会创建包含 category_id 和 product_id 字段的 categories_products 数据表。这两个字段的 :null 选项默认设置为 false,可以通过 :column_options 选项覆盖这一设置:
create_join_table :products, :categories, column_options: { null: true }
联结数据表的名称默认由 create_join_table 方法的前两个参数按字母顺序组合而来。可以传入 :table_name 选项来自定义联结数据表的名称:
create_join_table :products, :categories, table_name: :categorization
上面的代码会创建 categorization 数据表。
create_join_table 方法也接受块作为参数,用于添加索引(默认未创建的索引)或附加字段:
create_join_table :products, :categories do |t|
t.index :product_id
t.index :category_id
end
如果迁移名称中包含 JoinTable
,生成器会创建联结数据表:
|
上面的命令会生成:
|
传递修饰符
可以直接在命令行中传递常用的类型修饰符。这些类型修饰符用大括号括起来,放在字段类型之后。例如,运行下面的命令:
|
上面的命令会创建下面的迁移:
|
创建数据表
create_table
方法是最基础、最常用的方法,其代码通常是由模型或脚手架生成器生成的。典型的用法像下面这样:
|
上面的命令会创建包含 name
字段的 products
数据表(后面会介绍,数据表还包含自动创建的 id
字段)。
默认情况下,create_table
方法会创建 id
主键。可以用 :primary_key
选项来修改主键名称,还可以传入 id: false
选项以禁用主键。如果需要传递数据库特有的选项,可以在 :options
选项中使用 SQL 代码片段。例如:
|
上面的代码会在用于创建数据表的 SQL 语句末尾加上 ENGINE=BLACKHOLE
(如果使用 MySQL 或 MarialDB,默认选项是 ENGINE=InnoDB
)。
修改数据表
change_table
方法和 create_table
非常类似,用于修改现有的数据表。它的用法和 create_table
方法风格类似,但传入块的对象有更多用法。例如:
|
上面的代码删除 description
和 name
字段,创建 part_number
字符串字段并添加索引,最后重命名 upccode
字段。
修改字段
Rails 提供了与 remove_column
和 add_column
类似的 change_column
迁移方法。
|
上面的代码把 products
数据表的 part_number
字段修改为 :text
字段。请注意 change_column
命令是无法撤销的。
除 change_column
方法之外,还有 change_column_null
和 change_column_default
方法,前者专门用于设置字段可以为空或不可以为空,后者专门用于修改字段的默认值。
|
上面的代码把 products
数据表的 part_number
字段修改为 :text
字段。请注意 change_column
命令是无法撤销的。
除 change_column
方法之外,还有 change_column_null
和 change_column_default
方法,前者专门用于设置字段可以为空或不可以为空,后者专门用于修改字段的默认值。
字段修饰符
字段修饰符可以在创建或修改字段时使用:
limit
修饰符:设置string/text/binary/integer
字段的最大长度。precision
修饰符:定义decimal
字段的精度,表示数字的总位数。scale
修饰符:定义decimal
字段的标度,表示小数点后的位数。polymorphic
修饰符:为belongs_to
关联添加type
字段。null
修饰符:设置字段能否为NULL
值。default
修饰符:设置字段的默认值。请注意,如果使用动态值(如日期)作为默认值,那么默认值只会在第一次使时(如应用迁移的日期)计算一次。index
修饰符:为字段添加索引。comment
修饰符:为字段添加注释。
有的适配器可能支持附加选项
外键
尽管不是必需的,但有时我们需要使用外键约束以保证引用完整性。
|
上面的代码为 articles
数据表的 author_id
字段添加外键,这个外键会引用 authors
数据表的 id
字段。如果字段名不能从表名称推导出来,我们可以使用 :column
和 :primary_key
选项。
注意:Active Record 只支持单字段外键,要想使用复合外键就需要 execute
方法和 structure.sql
。
删除外键也很容易:
|
如果辅助方法不够用
如果 Active Record 提供的辅助方法不够用,可以使用 excute
方法执行任意 SQL 语句:
|
使用 change
方法
change
方法是编写迁移时最常用的。在大多数情况下,Active Record 知道如何自动撤销用 change
方法编写的迁移。目前,在 change
方法中只能使用下面这些方法:
add_column
add_foreign_key
add_index
add_reference
add_timestamps
change_column_default
(必须提供:from
和:to
选项)change_column_null
create_join_table
create_table
disable_extension
drop_join_table
drop_table
(必须提供块)enable_extension
remove_column
(必须提供字段类型)remove_foreign_key
(必须提供第二个数据表)remove_index
remove_reference
remove_timestamps
rename_column
rename_index
rename_table
如果在块中不使用 change
、change_default
和 remove
方法,那么 change_table
方法也是可撤销的。
如果提供了字段类型作为第三个参数,那么 remove_column
是可撤销的。别忘了提供原来字段的选项,否则 Rails 在回滚时就无法准确地重建字段了:
|
如果需要使用其他方法,可以用 reversible
方法或者 up
和 down
方法来代替 change
方法。
使用 reversible
方法
撤销复杂迁移所需的操作有一些是 Rails 无法自动完成的,这时可以使用 reversible
方法指定运行和撤销迁移所需的操作。例如:
# 异常执行down,正常执行up |
使用 reversible
方法可以确保指令按正确的顺序执行。在上面的代码中,撤销迁移时,down
块会在删除 home_page_url
字段之后、删除 distributors
数据表之前运行。也就是说:我先执行一遍,然后回滚,先删字段,再删表。
有时,迁移执行的操作是无法撤销的,例如删除数据。在这种情况下,我们可以在 down
块中抛出 ActiveRecord::IrreversibleMigration
异常。这样一旦尝试撤销迁移,就会显示无法撤销迁移的出错信息。
使用 up
和 down
方法
可以使用 up
和 down
方法以传统风格编写迁移而不使用 change
方法。up
方法用于描述对数据库模式所做的改变,down
方法用于撤销 up
方法所做的改变。换句话说,如果调用 up
方法之后紧接着调用 down
方法,数据库模式不会发生任何改变。例如用 up
方法创建数据表,就应该用 down
方法删除这个数据表。在 down
方法中撤销迁移时,明智的做法是按照和 up
方法中操作相反的顺序执行操作。下面的例子和上一节中的例子的功能完全相同:
|
对于无法撤销的迁移,应该在 down
方法中抛出 ActiveRecord::IrreversibleMigration
异常。这样一旦尝试撤销迁移,就会显示无法撤销迁移的出错信息。
撤销之前的迁移
Active Record 提供了 revert
方法用于回滚迁移:
|
revert
方法也接受块,在块中可以定义用于撤销迁移的指令。如果只是想要撤销之前迁移的部分操作,就可以使用块。例如,假设有一个 ExampleMigration
迁移已经执行,但后来发现应该用 ActiveRecord 验证代替 CHECK
约束来验证邮编,那么可以像下面这样编写迁移:
|
不使用 revert
方法也可以编写出和上面的迁移功能相同的迁移,但需要更多步骤:调换 create_table
方法和 reversible
方法的顺序,用 drop_table
方法代替 create_table
方法,最后对调 up
和 down
方法。换句话说,这么多步骤用一个 revert
方法就可以代替。
运行迁移
Rails 提供了一套用于运行迁移的 bin/rails
任务。其中最常用的是 rails db:migrate
任务,用于调用所有未运行的迁移中的 chagne
或 up
方法。如果没有未运行的迁移,任务会直接退出。调用顺序是根据迁移文件名的时间戳确定的。
请注意,执行 db:migrate
任务时会自动执行 db:schema:dump
任务,这个任务用于更新 db/schema.rb
文件,以匹配数据库结构。
如果指定了目标版本,Active Record 会运行该版本之前的所有迁移(调用其中的 change
、up
和 down
方法),其中版本指的是迁移文件名的数字前缀。例如,下面的命令会运行 20080906120000
版本之前的所有迁移:
|
如果版本 20080906120000
高于当前版本(换句话说,是向上迁移),上面的命令会按顺序运行迁移直到运行完 20080906120000
版本,之后的版本都不会运行。如果是向下迁移(即版本 20080906120000
低于当前版本),上面的命令会按顺序运行 20080906120000
版本之前的所有迁移,不包括 20080906120000
版本。
回滚
另一个常用任务是回滚最后一个迁移。例如,当发现最后一个迁移中有错误需要修正时,就可以执行回滚任务。回滚最后一个迁移不需要指定这个迁移的版本,直接执行下面的命令即可:
|
上面的命令通过撤销 change
方法或调用 down
方法来回滚最后一个迁移。要想取消多个迁移,可以使用 STEP
参数:
|
上面的命令会撤销最后三个迁移。
db:migrate:redo
任务用于回滚最后一个迁移并再次运行这个迁移。和 db:rollback
任务一样,如果需要重做多个迁移,可以使用 STEP
参数,例如:
|
这些 bin/rails
任务可以完成的操作,通过 db:migrate
也都能完成,区别在于这些任务使用起来更方便,无需显式指定迁移的版本。
安装数据库
rails db:setup
任务用于创建数据库,加载数据库模式,并使用种子数据初始化数据库。
4.3 重置数据库
rails db:reset
任务用于删除并重新创建数据库,其功能相当于 rails db:drop db:setup
。