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之後的提示信息
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