Rails开发细节《五》Migrations 数据迁移
1.简介
在rails中用migration可以很方便的管理数据库的结构。可以创建数据库,创建表,删除表,添加字段,删除字段,整理数据。
migration就是一系列的class,这些类都继承了ActiveRecord::Migration类。
- class CreateProducts < ActiveRecord::Migration
- def up
- create_table :products do |t|
- t.string :name
- t.column :description, :text
- t.timestamps
- end
- end
- def down
- drop_table :products
- end
- end
上面就是一个migration例子。up方法中的代码会在
rake db:migrate
之后执行。
down方法中的代码会在
rake db:rollback
之后执行。
t.timestamps会自动产生created_at和updated_at列。
还可以进行表结构修改。
- class AddReceiveNewsletterToUsers < ActiveRecord::Migration
- def up
- change_table :users do |t|
- t.boolean :receive_newsletter, :default => false
- end
- User.update_all ["receive_newsletter = ?", true]
- end
- def down
- remove_column :users, :receive_newsletter
- end
- end
rails3.1之后产生了一个新的方法change,主要用来创建表和列,不用写一对up和down了,使用rake db:rollback回滚的时候数据库不用down方法也知道如何做了。
1.1.migration提供了很多的方法
- add_column
- add_index
- change_column
- change_table
- create_table
- drop_table
- remove_column
- remove_index
- rename_column
如果想回滚migration对数据库造成的改变,可以使用rake db:rollback命令。
1.2.ActiveRecord支持的列类型
- :binary
- :boolean
- :date
- :datetime
- :decimal
- :float
- :integer
- :primary_key
- :string
- :text
- :time
- :timestamp
2.创建migration
2.1.创建model
rails generate model Product name:string description:text
创建的migration文件位于db/migrate目录,文件名称为yyyymmddmmss_create_products.rb。
- class CreateProducts < ActiveRecord::Migration
- def change
- create_table :products do |t|
- t.string :name
- t.text :description
- t.timestamps
- end
- end
- end
2.2.创建单独的migration
- rails generate migration AddPartNumberToProduct
- class AddPartNumberToProducts < ActiveRecord::Migration
- def change
- end
- end
- rails generate migration AddPartNumberToProduct part_number:string
- class AddPartNumberToProducts < ActiveRecord::Migration
- def change
- add_column :products, :part_number, :string
- end
- end
- rails generate migration RemovePartNumberToProduct part_number:string
- class RemovePartNumberFromProducts < ActiveRecord::Migration
- def up
- remove_column :products, :part_number
- end
- def down
- add_column :products, :part_number, :string
- end
- end
还可以添加多个列
- rails generate migration AddDetailsToProducts part_number:string price:decimal
- class AddDetailsToProducts < ActiveRecord::Migration
- def change
- add_column :products, :part_number, :string
- add_column :products, :price, :decimal
- end
- end
3.编写mirgation
3.1.创建表
- create_table :products do |t|
- t.string :name
- end
- create_table :products do |t|
- t.column :name, :string, :null => false
- end
如果数据库是mysql,还可以通过下面的语句指定使用的引擎,mysql默认的引擎是InnoDB。
- create_table :products, :options => "ENGINE=MyISAM" do |t|
- t.string :name, :null => false
- end
3.2.修改表结构
- change_table :products do |t|
- t.remove :description, :name
- t.string :part_number
- t.index :part_number
- t.rename :upccode, :upc_code
- end
删除name,description字段,添加part_number字段,在part_number字段建立索引,重命名upccode为upc_code。
3.3.辅助工具
t.timestamps可以自动添加created_at 和 updated_at列。
- #创建表的同时添加
- create_table :products do |t|
- t.timestamps
- end
- #给已经存在的表添加
- change_table :products do |t|
- t.timestamps
- end
还有一个帮助工具references,用来指明表的外键关系。
- create_table :products do |t|
- t.references :category
- end
上面的代码会在products表中添加一个外键字段category_id。
- create_table :products do |t|
- t.references :attachment, :polymorphic => {:default => 'Photo'}
- end
上面的代码不仅会在products表中添加外键字段attachment_id,还会添加attachment_type字段,string类型,默认值是Photo。
3.4.change方法的使用
change方法可以部分的替代up和down方法,数据库会自动的回滚。但是目前在change方法中只支持下面的migration。
- add_column
- add_index
- add_timestamps
- create_table
- remove_timestamps
- rename_column
- rename_index
- rename_table
如果要用其他的migration,你就需要自己写up和down了,不能再使用change了。
3.5.up和down方法的使用
down方法用来回滚数据库,你需要注意在down方法中的顺序,相对于up方法中的顺序。
- class ExampleMigration < ActiveRecord::Migration
- def up
- create_table :products do |t|
- t.references :category
- end
- #add a foreign key
- execute <<-SQL
- ALTER TABLE products
- ADD CONSTRAINT fk_products_categories
- FOREIGN KEY (category_id)
- REFERENCES categories(id)
- SQL
- add_column :users, :home_page_url, :string
- rename_column :users, :email, :email_address
- end
- def down
- rename_column :users, :email_address, :email
- remove_column :users, :home_page_url
- execute <<-SQL
- ALTER TABLE products
- DROP FOREIGN KEY fk_products_categories
- SQL
- drop_table :products
- end
- end
4.运行迁移
通过
- rake db:migrate
命令就可以执行迁移任务。
如果指定了版本,就会执行指定版本的up,change或者down方法。版本就是migration文件的前缀。用VERSION参数来指定版本号。
- rake db:migrate VERSION=20080906120000
拿上面的这个命令举例。如果20080906120000 比当前的版本大,会执行所有migration的up方法,包括20080906120000 中的up方法,不执行其他的migration方法。如果是小于,会执行所有migration的down方法,不包括20080906120000中的down方法。
4.1.回滚数据库
回滚上一次的数据库变更
rake db:rollback
回滚之前三次的数据库变更
rake db:rollback STEP=3
重做之前三次的数据库变更,重做之前会先回滚。
rake db:migrate:redo STEP=3
4.2.重置数据库
删除当前数据库,重新创建,重新执行migration。
- rake db:reset
4.3.执行指定的migration
- rake db:migrate:up VERSION=20080906120000
4.4.改变执行migration之后的提示信息
ethod | Purpose |
---|---|
suppress_messages | Takes a block as an argument and suppresses any output generated by the block. |
say | Takes a message argument and outputs it as is. A second boolean argument can be passed to specify whether to indent or not. |
say_with_time | Outputs text along with how long it took to run its block. If the block returns an integer it assumes it is the number of rows affected. |
- class CreateProducts < ActiveRecord::Migration
- def change
- suppress_messages do
- create_table :products do |t|
- t.string :name
- t.text :description
- t.timestamps
- end
- end
- say "Created a table"
- suppress_messages {add_index :products, :name}
- say "and an index!", true
- say_with_time 'Waiting for a while' do
- sleep 10
- 250
- end
- end
- end
5.在migration中使用model对象
假设有两个开发者,公用一个代码库。
其中一个休假了,另外一个还在工作,对数据库进行了修改。
- # db/migrate/20100513121110_add_flag_to_product.rb
- class AddFlagToProduct < ActiveRecord::Migration
- def change
- add_column :products, :flag, :boolean
- Product.all.each do |product|
- product.update_attributes!(:flag => 'false')
- end
- end
- end
- # app/model/product.rb
- class Product < ActiveRecord::Base
- validates :flag, :presence => true
- end
- # db/migrate/20100515121110_add_fuzz_to_product.rb
- class AddFuzzToProduct < ActiveRecord::Migration
- def change
- add_column :products, :fuzz, :string
- Product.all.each do |product|
- product.update_attributes! :fuzz => 'fuzzy'
- end
- end
- end
- # app/model/product.rb
- class Product < ActiveRecord::Base
- validates :flag, :fuzz, :presence => true
- end
添加了一些字段,添加了一些验证。
休假的同事回来了。
获取代码。
执行 rake db:migrate。
报错了。
- rake aborted!
- An error has occurred, this and all later migrations canceled:
- undefined method `fuzz' for #<Product:0x000001049b14a0>
因为在执行完第一个migration之后,验证model的时候,还没有执行第二个migration,也就还没有fuzz字段。
解决的办法就是在migration中添加空的model定义,阻止migration运行的时候验证model,因为空的model中没有验证规则,这样就migration就可以运行完毕。
由于在migration中包括更新字段的操作,还需要添加Product.reset_column_information来更新ActiveRecord中缓存的之前的空Product就可以了。
- # db/migrate/20100513121110_add_flag_to_product.rb
- class AddFlagToProduct < ActiveRecord::Migration
- class Product < ActiveRecord::Base
- end
- def change
- add_column :products, :flag, :integer
- Product.reset_column_information
- Product.all.each do |product|
- product.update_attributes!(:flag => false)
- end
- end
- end
- # db/migrate/20100515121110_add_fuzz_to_product.rb
- class AddFuzzToProduct < ActiveRecord::Migration
- class Product < ActiveRecord::Base
- end
- def change
- add_column :products, :fuzz, :string
- Product.reset_column_information
- Product.all.each do |product|
- product.update_attributes!(:fuzz => 'fuzzy')
- end
- end
- end
6.数据库的描述文件
在 db/schema.rb或者是db/*.sql文件中就是数据库的结构,这个文件不是用来修改的,自动生成,用来查看数据库的当前状态。
6.1.描述文件的类型
在config/application.rb文件中可以配置描述文件的类型,config.active_record.schema_format,值为 :sql 或 :ruby,默认是:ruby。如果是:ruby,文件的内容就是。
- ActiveRecord::Schema.define(:version => 20080906171750) do
- create_table "authors", :force => true do |t|
- t.string "name"
- t.datetime "created_at"
- t.datetime "updated_at"
- end
- create_table "products", :force => true do |t|
- t.string "name"
- t.text "description"
- t.datetime "created_at"
- t.datetime "updated_at"
- t.string "part_number"
- end
- end
本文转自 virusswb 51CTO博客,原文链接:http://blog.51cto.com/virusswb/1019513,如需转载请自行联系原作者