天天看点

Rails migration简介

migration好像是rails 1.0出的功能,到底它是什么什么东东?用来干吗的?且听下面分解. <o:p></o:p>

migration是一种分布环境下的数据库同步功能,它提供了: 1.独立于特定SQL的schema描述(当然是用ruby DSL啦).目前除了DB2外别的主流商用和开源数据库都被支持.(IBM真衰).显然,这种数据库独立的schema描述不可能包括数据库特定的细节, 所以在生产环境中需要再行调试优化. 2.schema和数据内容版本控制.我不是DBA啊(虽然曾经想过干这个),不知道有什么别的工具可以做数据库版本控制. 3.基于版本控制的分布式数据库同步. <o:p></o:p>

先来说说基于ruby DSL的schema描述巴,打打基础. 如果rails项目中的数据库已经设计配置好了,可以执行rake db:schema:dump(这是Rails1.1的rake新语法,以前是rake db_schema_dump) 然后会生成db/schema.rb文件.打开一看,基本结构类似这样的: <o:p></o:p>

<o:p> </o:p>

ActiveRecord::Schema.define(:version => 5) do<o:p></o:p>

  create_table "choices",:id => false, :force => true do |t|<o:p></o:p>

    t.column "name", :string,, :limit => 30, :default => 'idiolt'<o:p></o:p>

    t.column "content", :text, :limit => 100<o:p></o:p>

    t.column "question_id", :integer, :null => false<o:p></o:p>

  end<o:p></o:p>

...<o:p></o:p>

:version以后用了migrate才会看见,现在应该是空的,先不管这个. 如你所见这个表定义非常直观.需要说明的几个选项. <o:p></o:p>

:force代表强制覆盖,默认false后面执行db:schema:load把表结构导回数据库的时候如果表已经存在由这个选项决定是否覆盖. :id参数默认为true,也就是按照rails约定每个表自动建立id字段作为主键.false的话意思是你自己在schema中建立主键. :limit和:default不用解释了吧. :null也没啥好解释的,false代表着SQL里的NOT NULL <o:p></o:p>

你们已经看到db:schema:load了,恭喜,现在哪怕你不关心版本控制和分布式同步这些 "遥远的事". 你也可以从容在各种不同数据库直接移植表结构了.这可是DBA都头疼的事呢. 只要改一下database.yml的配置再执行rake db:schema:load就可以了. <o:p></o:p>

现在来说说版本控制和同步.数据库毕竟不是代码,除了schema外还有数据的.像上面这样 load导进去的话会覆盖掉所有的数据. 所以我们需要找一个对数据影响最小的方法来更新数据库. 这部分需要做些手工操作了: script/generate migration XXX 建立一个migration版本,这个操作会在db/migration下产生nnn_XXX.rb文件. 同时 generate model的时候也会顺带产生nnn_create_XXX.rb文件. 内容类似: <o:p></o:p>

<o:p> </o:p>

class CreatePortfolios < ActiveRecord::Migration<o:p></o:p>

  def self.up<o:p></o:p>

    create_table :portfolios do |t|<o:p></o:p>

      t.column :name, :string<o:p></o:p>

    end<o:p></o:p>

  end<o:p></o:p>

  def self.down<o:p></o:p>

    drop_table :portfolios<o:p></o:p>

  end<o:p></o:p>

end<o:p></o:p>

up方法定义这个版本该做什么,down定义怎么回滚版本. 这里up里面创建了一个表, 方法和上面schema.rb里的一样. 除此外更常用的是对字段和索引的修改方法, 如 add_column/rename_column/change_column等,详见api文档. 除了对表结构的修改,还可以做任何对数据的修改. 比如导入数据常用的先删掉index,然后再导入, 最后再重建index. <o:p></o:p>

定义好了migration后,我们可以执行rake db:migrate更新到最高的版本,或者指定回滚到某个版本: rake db:migrate version=n. migration自动根据当前版本判定该从哪回滚到哪. 在分布环境下,同步代码后只要执行一下就能保证数据库状态也是最新版本. <o:p></o:p>

engine/plugin也能使用migration,比如login engine,在不破坏用户已有数据库内容的条件下添加新的所需数据内容. <o:p></o:p>

另外需要注意的是每次使用migration后,db/schema.rb都会被刷新,所有手工对这个文件的修改都会丢失. <o:p></o:p>

migration默认是不支持外键的,原因是一部分数据库如MySQL ISAM和SQLite不支持外键,同时外键也会给Rails的test fixture导入数据造成麻烦(这个好像有个很麻烦的解决办法)。不过还是有个插件提供了外键的导出(rake db:schema:dump)和导入(rake db:schema:load)(可惜migration中无法正常使用)。 <o:p></o:p>

安装: <o:p></o:p>

<o:p> </o:p>

script/plugin install svn://caboo.se/plugins/atmos/activerecord_foreign_key_extensions<o:p></o:p>

语法: <o:p></o:p>

<o:p> </o:p>

add_foreign_key_constraint table, foreign_key, reference_table, reference_column, :name => optional_constraint_name, :on_update => optional_on_update_action, :on_delete => optional_on_delete_action<o:p></o:p>