转载:http://hi.baidu.com/fengwill/blog/item/c80a8d829b0310aa6d811921.html
命令列表
rake db:migrate
命令作用:将所有未实施的迁移任务都实施到目标数据库上。
rake db:migrate VERSION=20080906120000
命令作用:撤销迁移任务到指定版本
迁移版本信息可以在所创建库(如elvuel_deployment数据库)下schema_info表中查看到相关记录信息。
rake db:migrate VERSION=0
命令作用:将数据库返回到最初状态(即,只有schema_migrations表,其他表都删除)
rake db:migrate VERBOSE=false
命令作用:屏蔽migration的输出
rake db:rollback
命令作用:回滚到最近的一次migration执行的状态
rake db:rollback STEP=3
命令作用:回滚最近的3次的迁移任务
一个在开发阶段很常用的命令redo:
rake db:migrate:redo
命令作用:重做迁移
rake db:migrate:redo STEP=3
rake db:migrate:down VERSION=9999999999
================================================ agile with rails ================================================
Rails鼓励敏捷,迭代的开发风格。我们不会第一次就期望得到正确的东西。相反我们会写测试,并与客户沟通以加强我们对事物的理解。
要做到这些,我们就需要大量的实践工作。我们写测试来帮助规划我们接口,以便我们能安全地进行修改,我们对应用程序源文件使用版本控制,允许我们回说溯错误,并可监视我们每天的改动。
但对于应用程序修改的另一方面来说,我们不能直接使用版本控制来管理。它就是我们在开发过程中,对应用程序中数据库schema的管理:我们添加一个表,重命名列名称等等。数据库的修改需要应用程序的代码。
很久以来,这一直是个问题。开发者(或数据库管理员)使用schema进行修改。但是,如果应用程序代码可以回溯到一个先前版本,但数据库schema的修改却不是同步的。因为数据库本身没有版本信息。
不久前,开发者以提出了多种解决这个问题的途径。一个schema保持了数据库定义语言(DDL),而DDL在版本控制下以源代码方式定义了schema。无论何时只要你修改了schema,你通过编辑这个文件来反映做出的修改。那么你会中止你的development数据库,并重新从你写的DDL中创建schema。如果你需要回溯到一周之前的话,那么你就会按这些步骤来检查应用程序代码和DDL:当你重新从DDL创建schema时,你的数据库会得到及时的回溯。
除了…..因为你每次应用DDL时,你都会中止数据库,你会丢失放在development数据库内的数据。如果我们能将数据库从版本x迁移到y就更好了?不错,Rails的migration迁移就可让你做到点。
让我们先在抽象层上看看migration迁移。假设我们有个存储定单数据的order表。有一天,我们的客户要求我们为每个定单上添加用户的邮件地址。这就包括了对应用程序代码数据库shema的修改。要处理它,我们创建了一个数据库migration迁移,在其内描述“给orders表添加一个e-mail字段”。这个migration迁移放在一个单独的文件内。我们将其与另外的应用程序文件一起放在版本控制下。然后我们对数据库使用这个migration迁移,结果列被添加到现有的orders表内。
如何正确地为数据库完成一个migration迁移呢?每次migration迁移都会有一个序号与其关联。这些序号从1开始---每个新的migration迁移都会得到下一个有效序号。Rails会记住应用给数据库migration迁移的最后一个序号。那么你会问,何时应用新的migration迁移来更新schema,它将数据库schema的序号与有效的migragion迁移序号进行比较。如果它发现migration迁移的序号大于数据库的schema,它就会应用migration迁移。
但是我们如何回溯一个先前版本的schema呢?我们通过让每个migration迁移都是可回溯的来做到这一点。每个migration迁移实例上都包含两个指令集。一套告诉Rails在应用migration迁移时对数据库做出什么修改,另一套则告诉Rails如何回溯这些修改。在orders表例子中,是添加e-mail列到表与移除列的回溯两部分。现在,要回溯一个schema,我们只要简单地告诉Rails我们需要数据库schema序号就可以了。如果当前数据库schema有个比目标序号更高的序号,则Rails会接受带有数据库当前序号的migration迁移,并使用它进行回溯。这会从schema中移除migration迁移的修改,并降低数据库的序号。它会反复执行此过程直到得到期望的数据库版本。
16.1 创建与运行Migration迁移
Migration迁移是应用程序db/migrate目录下的一个简单的Ruby源文件。每个migration迁移文件的名字(默认地)以三个数字和一个下划线开头。这些数字是migration迁移的关键,因为它们定义了应用哪个migration迁移的次序,它们各个migration迁移的版本号。
Depot应用程序的db/migrate目录看起像这样:
depot> ls db/migrate
001_create_products.rb 005_create_orders.rb
002_add_price.rb 006_create_line_items.rb
003_add_test_data.rb 007_create_users.rb
004_add_sessions.rb
虽然你可以通过手工来创建这些migration迁移文件,但使用一个生成器会更容易(这会减少错误)。就像在创建Depot应用程序时看到的,实际上有两种用于创建migration迁移文件的生成器:
1、模型生成器(model generator) 创建的migration迁移,用于创建与模型关联的表(只要你不使用skip-migrations选项)。如下面例子所示,创建了名为discount的模型,同时也创建了名为add_create_discounts.rb的migration迁移。
depot> ruby script/generate model discount
exists app/models/
exists test/unit/
exists test/fixtures/
create app/models/discount.rb
create test/unit/discount_test.rb
create test/fixtures/discounts.yml
exists db/migrate
create db/migrate/014_create_discounts.rb
2、你也可以用生成器只创建migration迁移本身。
depot> script/generate migration add_price_column
exists db/migrate
create db/migrate/015_add_price_column.rb
稍后,我们会从Migration的解剖图开始,我们会看到migration迁移文件是什么。但现在,我们还是先看看如何运行migration迁移。
一、Running Migrations
使用Rake的db:migrate任务来运行migration迁移。
depot> rake db:migrate
下面看看发生了什么,让我们深入到Rails的内部。
在Rails数据库内部,migration迁移代码管理一个名为schema_info的表。该表只有一个列,名为version,并且它只有一行记录。此schema_info表被用于记住当前的数据库版本。
当运行rake db:migrate时,任务首先会查看schema_info表。如果该表不存在,它将创建一个并且生成版本号为0的记录。若该表存在,则从表中读取版本号。
然后migration迁移代码查看db/migrate目录下的所有migration迁移文件。如果有序号(文件名的前导数字)大于当前数据库的版本号,那么会为数据库依次应用每个migration迁移文件。在最后一个migration迁移文件应用后,schema_info表的版本号会被更新为该文件的序号。
如果我们这一序号上再次运行migration迁移,则不会发生任何事。因为数据库内版本号已等于最高序号migration迁移文件的序号,所以不会应用任何migration迁移文件。
但是,如果我们随后创建了一个新的migration迁移文件,它就会有个大于数据库版本号的序号。如果我们随后运行该migration,则这个新的migration迁移文件会被运行。
你可以通过给rake db:migrate命令行应用VERSION=参数来为数据库强制指定一个特定的版本号。如果你给出的版本号高于数据库的版本号,则会以运行数据库版本的migration开始,以你指定的版本migration结束。
但是,如果命令行的版本号小于当前数据库的版本号,那么会发生不同的事情。在这些情形下,Rails查找与数据库版本匹配的migration迁移文件,并且回溯它。然后它降低版本号,查找匹配文件,再回溯它,等等,直到版本号匹配你在命令行上指定版本号。也就是说,migration迁移被向后应用以回溯schema回到你指定版本。
16.2 Migration解剖图
你通过创建Rails类ActiveRecord::Migration的子类来写一个migration迁移。你创建的类至少应该包含两个类方法up()和down()。
class SomeMeaningfulname < ActiveRecord::Migration
def self.up
# ...
end
def self.down
# ...
end
end
在down()方法回溯修改的同时,up()方法有责任为这个migration迁移应用schema修改。让我们做的更具体些。这儿是个migration迁移,它为orders表添加一个e_mails列。
class AddEmailColumnToOrders < ActiveRecord::Migration
def self.up
add_column :orders, :e_mail, :string
end
def self.down
remove_column :orders, :e_mail
end
end
看看down()方法的回溯是如何影响到up()方法的。
一、Column Types (列类型)
传递给add_column的第三个参数指定了数据库列的类型。 在前面例子中,我们指定e_mail列是 :string 类型。但这么做意味着什么呢?典型地数据库没有 :string 列类型。
回忆一下,Rails试图让你的应用程序不依赖于支持它运行的数据库:你可以开发使用MySQL,如果愿意也可开使用Postgres数据库的应用用程序。但不同的数据库为列类型使用了不同的名字。如果你在migration迁移中使用了一个MySQL列类型,那么这个migration迁移对Postgres数据库就不会工作。所以Rails migration迁移将你从基础数据库类型系统隔离开而使用逻辑类型。如果你迁移一个MySQL数据库,那么 :string 类型将创建一个varchar(255)类型的列。在Postgres上,同样的迁移会添加一个varing(255)类型的列。
由migration迁移支持的类型是: :binary,:boolean,:date,:datetime,:float,:integer,:string,:text,:time,和:timestamp。256页的图16-1显示了在Rails内数据库适配器对这些类型的缺省映射。使用这个图,你在一个migration迁移内,用 :integer声明的列在MySQL内基于类型int(11),在Oracle内基于number(38)来工作。
当在一个migration迁移内定义一个列时,你可指定三个选项。每个选项由key=>value对给出。
1、:null => true or false
如果为 true,则基础列被添加一个不能为null的约束(如果数据库支持的话)。
2、:limit => size
设置字段尺寸的限制。这基本上出现在用string创建数据库的列时。
3、:default => value
为列设置缺省值。如果你传递一个Ruby值(或Ruby表达式),那个值会变成列的默认值。对一些数据库,你也可以传递一个包含数据库指定表达式的字符串。
例如,指定 add_column :orders, :placed_at, :datetime, :default => Time.now 将在migration迁移运行时,设置列的默认值为日期和时间,
指定add_column :orders, :placed_at, :datetime, :default => "now()" 会设置MySQL now()函数为默认值,因此当前日期时间将被插入到任何新的行内。后面的语法很明显是数据库指定的。
下面是一些使用migration迁移类型和选项的例子:
add_column :orders, :name, :string, :limit => 100, :null => false
add_column :orders, :age, :integer
add_column :orders, :ship_class, :string, :limit => 15, :default => 'priority'
二、Renaming Columns (重命名列)
在我们重构代码时,通常会修改我们变量的名字,以让它们更有意义。Rails的migration迁移也允许我们对数据库的列名字这样做。例如,在添加了一周之后,我们可能认为e_mail并不是那个新列最好的名字。我们可以创建一个migration迁移来重命名它。
class RenameEmailColumn < ActiveRecord::Migration
def self.up
rename_column :orders, :e_mail, :customer_email
end
def self.down
rename_column :orders, :customer_email, :e_mail
end
end
注意重命名列并不会删除任何与该列相关的数据。但是要小心并不是所有的数据库适配器都支持重命名的。
三、Changing Columns (更改列)
有时候你可能需要更改列的类型,或改变与列关联的选项。它与你使用add_column的方式一样,但指定是一个现有列的名字。我们假设order类型列当前是个integer,但我们需要更改它为string。我们想保持现有数据,所以一个123的order类型数据将变成字符串”123”。随后,我们就可使用非整数值如”new”和”existing”。
Changing from an integer column to a string is easy:
def self.up
change_column :orders, :order_type, :string, :null => false
end
但是,向相反方向的转换却是个问题。我们可能会试着这样写down() migration迁移:
def self.down
change_column :orders, :order_type, :integer
end
但是如果我们的应用程序已在这个列中存储了像”new”这样的数据,down()方法将会丢失数据 --- “new”不能被转换成一个整数。如果这是可接受的,那么migration迁移就接受它。然而,如果我们想创建一个单向的migration迁移 --- 那么它就是不可逆转的 --- 你会中止向下的migration迁移。在这种情况下,Rails提供了一个你可以抛出的特殊异常。
class ChangeOrderTypeToString < ActiveRecord::Migration
def self.up
change_column :orders, :order_type, :string, :null => false
end
def self.down
raise ActiveRecord::IrreversibleMigration
end
end
===========================
指定某个环境的数据库:
rake db:migrate RAILS_ENV="rits"
===========================