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