Rails 数据库迁移(Migrations)
2012-06-24 12:47
351 查看
Rails 数据库迁移(Migrations)
数据库迁移(Migrations)提供了一些便利的方法让你有条理地修改数据库。虽然说直接 编写SQL也能修改数据库,但是这样你不但必须通知其他的开发者去执行一样的步骤,而且你 也得一直注意下次部署的时候和在正式上线的产品版服务器上面追踪并执行这些操作。Active Record 会自动追踪哪些 Migrations 已经执行过、哪些还没执行。所以,你只要更新 你本地的代码然后执行 rake
db:migrate ,其他的就交给 Active Record ,它会自己搞懂该 跑哪些 Migrations 。还有,它也会自动更新db/schema.rb 文件,让它与修改后的数据库结构同步。
有了 Migrations ,你就可以用Ruby来写这些数据库变更。一件很棒的事情是 Migration 是独立 于数据库系统的(和大多数 Active Record 的功能一样),也就是说,你不用烦恼各种数据 库的语法差异,像是 SELECT
* 的各种写法之类的。(当然,如果要针对某个特定的数据库 系统编写特定的功能的话,你也可以直接编写原始的SQL语句)。例如,你可以在开发阶段 使用SQLite3,在正式上线阶段则使用MySQL,它会自动处理好两者间的语法细节。
在这个指南中,你将会了解到:
用于创建migration的生成器
Active Record 所提供用于操纵数据库的方法
用于操纵migrations的Rake任务
Migrations跟数据库纲要文件(schema.rb)的关联
目录
剖析迁移任务的构造Migrations也是类
关于文件名
迁移任务变更
支持的数据类型
创建一个迁移任务
建立一个 Model
建立一个独立的迁移任务
编写一个迁移任务
建立数据表
创建连接表
变更数据表
特殊方法
使用change方法
使用up/down方法
执行Migrations
回滚(Rolling Back)
重置数据库
执行指定的migration
修改执行migration时的提示结果
在Migrations中使用Models
导出数据库纲要(Schema Dumping)
Schema文件的作用?
数据库纲要(Schema)的导出类型
导出数据库纲要(Schema)以及版本控制
Active Record 与 Referential Integrity(参照完整性)
1 剖析迁移任务的构造
在我们深入介绍migration的细节之前,我们先看下列例子,了解我们能怎么写 迁移任务:string 类型字段和一个名为 description 的
text 类型字段。与此同时,一个名为 id 的字段也会被添加,这个字段是默认添加,我们不需要另外请求。
另外 Active Record 所需要的时间戳( timestamp )字段( created_at 和 updated_at )也会被自动添加。而要取消这个任务只需简单地把这张表删除掉即可。
数据迁移不仅可以胜任修改数据库架构,你还可以用它来修复数据库中的错误数据或者添加新字段。
这个迁移为 users 表添加了一个 receive_newsletter 字段,并设定新的
user 创建 时它的默认值为 false ,对于数据库中已存在的
users 我们使用 User 模型来把他们的这个 标志位设为 true。
Rails 3.1 为数据迁移提供了 change 方法使它变得更精简。这个方法主要用于一
些构造性的migrations(例如添加新字段或者新的表),它能知道怎么样迁移你的数据库 并在你需要回滚的时候恢复回去而并不需要写一个分开的down方法。
1.1 Migrations也是类
Migration 是继承 ActiveRecord::Migration 的一个子类,它实现了两个方法: up (执行需要的改变)和 down (恢复所做的改变)Active Record 提供以下独立于数据库的方法,用来执行普通数据定义任务的方法:
add_column
add_index
change_column
change_table
create_table
create_join_table
drop_table
remove_column
remove_index
rename_column
如果你需要为你的数据库完成一些特殊的任务(例如强制创建一个 外键 ), 这时候 execute 方法允许你执行任意的 SQL 语句。一个migration同时也是一个规则的
Ruby 类, 因而你不限于使用以上的方法。例如在添加完新字段后你可以编写代码去为数据库中已经 存在的记录设定这个字段的值(有需要的话利用你的 Models )。
某些数据库支持变更数据库纲要( schema )的语句的事务( transactions ),例如 PostgreSQL 或 SQLite3 ,它们的 migrations 就会包含在事务中处理。相反的,若你的数据库系统(如 MySQL ) 不支持的话,那么当 migration 失败了,发到一半的 schema ,并不会随着事务的取消而自动 回滚(roll back)。你必须手动挑出这些已经改动的部分。
1.2 关于文件名
Migrations 是以文件形式存储在 db/migrate 目录中,每一个迁移任务都作为一个文件。文件名的格式为 YYYYMMDDHHMMSS_create_products.rb ,文件名前面是一个UTC时间截,
接下来以下划线连接 migration 的名称。migration 的类名是遵循驼峰式命名格式,它跟 文件名的后半部分是一样的。例如 20080906120000_create_products.rb 这一个
migration 应该定义了一个名为CreateProducts 的类,而 20080906120001_add_details_to_products.rb 则定义了一个AddDetailsToProducts的类。如果你需要修改文件名,你就 必须 修改文件中的类名,不然会报
missing class 的错误。
Rails 内部在辨别 migration 的时候只会用到编号(前面的时间截)的部分。在 Rails2.1 之前 的版本, migration 的编号是从1开始递增的,这样做法,在多人开发时很容易造成编号冲突, 一旦冲突了就需要将 migration 回滚(rollback)并重新编号。如果你坚持想要用回原来的这种 编号结构,可以在config/application.rb 中加入下面这一行:
举例来说:爱丽丝新增了 20080906120000 和 20080906123000 这两个
migration ,而鲍勃 接下来新增了 20080906124500 并执行了它。当艾丽丝完成并提交后,鲍勃同步了最新的
代码。这时鲍勃执行 rake db:migrate,虽然鲍勃新增的
migraion 的时间截比较新,但 Rails会知道艾丽丝的这两个migration还没执行,它会自动执行对应的方法。
Of course this is no substitution for communication within the team. For example, if Alice’s migration removed a table that Bob’s migration assumed to exist, then trouble would certainly strike. 当然,团队内的沟通是必不可少的。例如爱丽丝的 migration 移除了鲍勃的 migration 会用到 的表,那肯定会出问题的。
1.3 迁移任务变更
有时你在写迁移任务的时候可能会不小心写错,如果你已经执行了这个迁移任务,那么, 你就不能单纯地把它修改一下再重新执行一次, Rails 会认为这个迁移任务已经执行过了, 所以执行 rakedb:migrate 时不会做任何操作。你应该先把写错的那个迁移任务回滚(可以执行 rake
db:rollback),然后修改你的migration再执行 rake
db:migrate 去 执行正确的版本。
一般来说,最好不要修改现有的迁移任务,因为这样做可能会给你跟你的同事带来很多 麻烦,特别是这个迁移任务已经在正式上线的服务器上执行过的话。你应该写一个新的 迁移任务来做数据库变更。如果这个迁移任务还没有进入版本控制(也就是说这些变更还 没有发布出去),那么直接修改还是可以的。
1.4 支持的数据类型
Active Record 支持的数据类型包括::binary
:boolean
:date
:datetime
:decimal
:float
:integer
:primary_key
:string
:text
:time
:timestamp
这些类型会自动对应到数据库,例如在MySQL下,:string 类型会对应到 VARCHAR(255)。
如果你要建立Active Recore不支持的数据类型,那可以用「non-sexy」语法,比方说:
2 创建一个迁移任务
2.1 建立一个 Model
Model和 scaffold的generators(生成器)在新建model的时候会自动生成对应的migration。 这个migraion里已经把建立数据表的步骤都写好了。如果生成的时候告诉Rails需要哪些字 段,Rails会把新增字段的代码都一起生成好。例如执行以下代码Active Record 会在 新增数据和更新时自动更新时间)。
2.2 建立一个独立的迁移任务
如果你建立迁移任务不是为了新增 Model,而是为了其他目的(例如为现在的数据库表添加 字段),那可以只用迁移任务的生成器:一串字段名和类型的清单,那么这个migration就会含有对应的 add_column 和 remove_column 语句。
The generated migration file for destructive migrations will still be old-style using the upand down methods.
This is because Rails needs to know the original data types defined when you made the original changes.
3 编写一个迁移任务
当你建立一个migration文件时,接下来就是我们要做的了。
3.1 建立数据表
一般要建立数据表可以用迁移任务的 create_table 方法。典型的用法如下:这里也会自动加上一个id字段)。
这段代码块可以让你在数据表中新增字段。新增的的方法有格式,第一种,也是相对传统的 写法,如下:
建立对应类型的字段。至于在后面添加的参数是一样的。
Model 的格式)。如果你根本就不要主键(例如使用多对多连接 HABTM的数据表时),那就传入 :id
=> false 。另外,如果要传入某个特定数据表的设定, 你可以在 :options选项中加上一个SQL片段。例如:
MySQL 的话,预设 是ENGINE=InnoDB)
3.2 创建连接表
迁移任务的 create_join_table 方法可以创建一张多对多的连接表,经典的写法如下:你可以通过 :table_name 字段去定义自己想要的数据表名称。例如:
默认情况下,create_join_table 将会创建两个不包含参数的字段,不过你也可以通过:column_options 来指定这些参数。例如:
3.3 变更数据表
要变更现有的数据表,可以用 create_table 的类似方法 change_table。它的用法跟create_table 差不多,但它的代码块有更多的方式。例如:为其添加了索引。最后重命名了 upccode 字段。
3.4 特殊方法
有些功能很常用,例如 created_at 和 updated_at 字段,为此,ActiveRecord 提供了一 些捷径:
此外:
另一个特殊的方法是 references (也可以写成 belongs_to )。它最基本的的功能就是
增加可读性。
的名称而不是字段名。Active Record 会自动在model名称的后面加上 _id 。若你需要用到
多态的belongs_to 关联时,那么 references 会把两个所需的字段都加进去。
references 辅助方法实际上不会帮你建立外键约束。你可能需要通过execute方法
或者用能够加入"外键支持":#7 的插件。
如果Active Record提供的辅助方法不能满足你的需求,你可以使用execute方法来执行任意
的SQL语句。
要想知道各个方法的细节和范例,请参考API文档,特别是关于ActiveRecord::ConnectionAdapters::SchemaStatements (提供了在up和down中可使用的方法)、 ActiveRecord::ConnectionAdapters::TableDefinition (提供了在create_table所产生的对象中可使用的方法)、以及 ActiveRecord::ConnectionAdapters::Table (提供了在create_table所产生的对象中可使用的方法)。
3.5 使用change方法
在一些情况下,Rails会知道如何去恢复所做的改变,使用change方法可以让我们不用同时写up和down方法。目前来说change方法只支持以下migration的定义:
add_column
add_index
add_timestamps
create_table
remove_timestamps
rename_column
rename_index
rename_table
如果你需要使用其他方法,那么你就不能使用change方法而需要同时写up和down方法。
3.6 使用up/down方法
Migration里面的down方法能复原up方法所造成的变更。也就是说如果执行了up然后再执行down,那么数据库的schema应该会没有改变。所以说,如果用up建立一个数据表,
就应该在down方法中删除它。明智的做法会使用跟up完全相反的顺便来做这些事情。
例如,
来如果有人想恢复你的migration时就会出现错误信息,显示它无法执行。
4 执行Migrations
Rails提供一系列rake任务来执行migrations。第一个跟migration相关的rake任务是 rakedb:migrate。它最基本有用法就是单纯地执行所有还没执行migrations的up或者 change方法。若所有migrations都执行过了,它就会直接结束。执行的顺序是按照migration 的日期。
值得注意的是执行db:migrate也会一起执行db:schema:dump,去更新db/schema.rb文件,
以便跟数据库的结构同步。
如果你要migrate到某个特定版本,Active Record会执行所需的migrations(up,down,change) 直到到达指定的版本为上。所谓版本就是migration文件名前面的那串数字。例如要迁移到 版本20080906120000,只需执行:
所有版本。如果是往下迁移的话则会执行所有down方法,但不包括指定版本本身。
4.1 回滚(Rolling Back)
另一个常见的任务是回滚最后一个版本。比如你不小心打错了要修正。输入回滚命令时可以 不用输入先前版本的版本号,直接这样就行了:一个STEP参数:
要回滚然后重新执行最后一个migration的话可以直接执行db:migrate:redo。如果要回滚
重新执行的不止一个版本时可以用STEP参数,就跟db:rollback的用法一样:
4.2 重置数据库
最后是db:reset任务,它会删除数据库,然后重新建立数据库并在重新建立的数据库中载入当前的schema。
所谓的载入schema跟执行全部的migrations是不一样的,请参照: schema.rb 。
4.3 执行指定的migration
如果你需要执行一个指定的migration的up或down方法,那么你可以用db:migrate:up和db:migrate:down这两个任务。你只需指定版本号,就可以触发它的up或down方法:没有跑过,所以,如果Active Record认为20080906120000已经跑过,那么执行 db:migrate:up
VERSION=20080906120000将不会做任何操作。
4.4 修改执行migration时的提示结果
默认情况下,migrations会告诉你它们在做什么,花多少时间。例如建立数据库并且添加 索引(index)的话会产生以下输出结果:Method | Purpose |
---|---|
suppress_messages | 阻止这个代码块的任何输出结果 |
say | 输出一段文字(第二个参数可以指定是否要缩排) |
say_with_time | 输出一段文字,以及这个代码块需要花多少时间才能跑完。如果代 码块中回传了一个整数,这个数代表受影响的数据(rows)有多少。 |
db:migrate VERBOSE=false就可 以阻止所有的输出结果。
5 在Migrations中使用Models
在migration中不管你是新增数据或者更新数据,多少都会用到一个model。毕竟model存在 就是为了方便地处理我们的数据。这当然是没问题的,只是有些地方需要留意一下。比方说,当model使用不正确的数据库的字段或者使用的字段是后来的migration创建的时候 就会出现问题。
根据这个例子,当爱丽比和鲍勃在同一份代码上工作,这份代码包含一个Product的模型:
鲍勃正在放假期。
爱丽丝创建了一个migration给products表添加一个新的字段并为它初始化。她同时在 Product模型为这个新的字段添加了验证(validation)。
鲍勃从他的假期回来后他做了以下操作:
更新源代码 – 包含了艾丽丝添加的那两个migrations和最新版本的Product model。
执行rake db:migrate命令来执行还未执行的migrations(包含那个更新productmodel
的migration)。
Migration执行失败,因为当model尝试保存的时候它会去验证那两个新增加的字段,而这些 字段在第一个migration执行的时候还没有添加数据库。
当使用假的(faux)model时,我们可以直接调用Product.reset_column_information 来更新ActiveRecord中Product模型的缓存从而在数据库中更新数据。
如果爱丽丝能这样做,那么将不会有问题发生。
6 导出数据库纲要(Schema Dumping)
6.1 Schema文件的作用?
虽然我们用Migrations来定义database schema(数据库纲要),但是我们却不能一次看到完 整精确的schema。这个机制是由db/schema.rb或是ActiveRecord检验数据库后所生成的 SQL文件来担当。它们设计出来不是用来编辑的,只是纯粹代表着数据库的schema现况。
当我们要部署新的应用程序时,并不需要把整个migrations历程全部重跑一遍,我们只需要 把当前的schema载入新的数据库就可以了。这样做会更快更简单。
例如,这就是建立一个测试用数据库所做的操作:把当前开发环境下的数据库导出来( 看是db/schema.rb或db/development.sql都行),然后再载入到测试数据库。
另外,如果要快速浏览Active Record 对象中有哪些属性也可以通过schema文件。关于 Active Record对象属性的信息并不在model的代码中,而且可能会散布在多个migrations 之间,但是最终都会整理在schema文件中。其次,有个插件叫做"annotate_models":https://github.com/ctran/annotate_models , 可以把schema的结果自动用注释的方式放在每个model的上面,有需要也可以看一下。
6.2 数据库纲要(Schema)的导出类型
导出的schema有 :sql 和 :ruby 两种方式,可以通过 config/application.rb 文件中的config.active_record.schema_format 来设置。如果设定成:ruby的话,schema就会存在db/schema.rb里面。这个文件看起来像是一个
超大的migration。
Active Record支持的数据库系统,它都可以载入。如果你的应用程序要发布到多个数据库 系统,这点会非常有用。
不过,这也是有考量的:db/schema.rb没办法表达出特定数据库所专属的功能,像是外键
约束(foreign key constraints)、触发(triggers)或是预存程序(stored procedures)。 虽然migration里面可以执行自定义的SQL语句,但是schema dumper却无法从数据库中将它 们重组回来。如果你要用到这个自定义SQL的功能,那么就必须把schema的格式设定成:sql。
设定成:sql的话就不是用Active Record来导出Schema了,而是用该数据库系统的专门工具,
从db:structure:dump这个Rake任务导进db/#{Rails.env}_structure.sql里面。比方说,
用PostgreSQL的话,就是用pg_dump这个工具。用MySQL的话,则是各个数据表的+SHOW CREATETABLE+输出结果。载入这些schema只不过是执行了文件里面的SQL语句而已。
虽然用:sql方式可以完美地复制数据库结构,不过如果换一个不同的数据库系统那就往往
没办法将schema写入新的数据库了。
6.3 导出数据库纲要(Schema)以及版本控制
因为schema dumps是数据库结构的精确来源,这么重要的东西,强烈建议你把它加入到版本 控制系统内。
7 Active Record 与 Referential Integrity(参照完整性)
所谓的 Active Record 之道,认为有头脑的应该是 models ,而不是数据库。因此,像是触发器 (triggers)或外键约束(foreign key constraints)这类偏向数据库功能,在 Rails 中就 比较不常使用。在 model里面要确保数据库完整性,可以用 validates
:foreign_key, :uniqueness => true来做验证。Model里面,数据库关联(associations)上有个:dependent的选项,可以设定
成当父对象删除(destroy)的时候,自动连其子对象也一起删除。不过就像其他应用程式层级 操作的东西一样,这并不能保证数据表之间的参照完整性,所以有些人用外键约束来增进 这个功能。
关于外建约束的功能,Active Record 并没有提供可以直接编辑它的工具,不过还是可以用 execute方法来执行任意的
SQL。也有不少的插件,可以帮 Active Record 加上外键的支持 (还可以把外键的设定全部显示在 db/schema.rb),像是这个"foreigner":https://github.com/matthuhiggins/foreigner
关于 Rails Guides 中文
你也可以为 Rails Guides 中文做出贡献.如果你发现了任何翻译上的错误或者不足.请到 GitHub 帐号 clone 并且 fork 一份进行更正提交,我们会很快做出回应。 如果你发现了任何代码上的 bug 或者不足,你可以到 docrails 进行反馈与更正。
如果你发现了某一篇文章已经过时了并且并没有做出相应的提示,你也同样可以在 这里 上发起一个请求来提醒我们。
如果对 Rails 有什么建议的话去 这里.
当然你不管你有什么想要说的都可以发到 Ruby China 讨论或者 mail我.欢迎加入到
Ruby on Rails 社区!
相关文章推荐
- Ruby On Rails--Active Record Migrations(数据库迁移)
- Ruby on rails 实战圣经:数据库迁移 - Migrations
- Ruby on Rails,rake工具使用和数据库migrations迁移的概念
- Ruby on Rails,rake工具使用和数据库migrations迁移的概念
- ruby on rails 【语法大全2】【模型(Models)】数据库迁移(Migrations)
- EF Code First Migrations数据库迁移
- Laravel 5框架学习之数据库迁移(Migrations)
- 在.NET中使用Rails风格的数据库迁移方式
- Rails中常见的数据库迁移命令总结
- EF Code First Migrations数据库迁移
- Ruby on Rails,数据库迁移命令和迁移任务编写
- laravel 数据库迁移Migrations(命令行)
- rails 迁移migrate model对应的数据库字段类型
- EF Code First Migrations数据库迁移
- EF Code First Migrations数据库迁移
- EF Code First Migrations数据库迁移
- ruby on rails入门笔记(1)-数据库迁移
- 对同一个项目下的多个数据库Context进行迁移Migrations
- EF Code First Migrations数据库迁移
- Ruby on Rails,数据库迁移命令和迁移任务编写