當你使用rails generate model產生指定的資料庫模型物件時,會產生幾個檔案:
invoke active_record
create db/migrate/20111214072231_create_messages.rb
...
其中20111214072231_create_messages.rb這個檔案,前半段是時間戳記,格式為YYYYMMDDHHMMSS,Rails以時間戳記來記錄哪個遷移rb檔案已經執行過,記錄用的表格是schema_migrations,欄位為version。
如果db/migrate中同時存在兩個以上的遷移用rb,時間戳記會決定執行順序,擁有較早時間戳記的遷移rb檔案會先執行,執行過的rb檔案,其時間戳記會記錄於資料庫中"schema_migrations"."version",已執行過的rb檔案不會再執行。
舉例來說,db/migrate中同時存在20120116064658_xx.rb與20120116064707_oo.rb,使用rake db:migrate會依序執行20120116064658_xx.rb與20120116064707_oo.rb;如果A使用者先在db/migrate中放了20120116064707_oo.rb,使用rake db:migrate執行過後,"schema_migrations"."version"會記錄20120116064707,B使用者再於db/migrate置入20120116064658_xx.rb,使用rake db:migrate時,雖然20120116064658_xx.rb的時間戳記比20120116064707_oo.rb早,仍只會執行20120116064658_xx.rb。
可以使用rake db:rollback撤消已執行過的遷移rb檔案,撤消順序是依據"schema_migrations"."version"中記錄的時間戳記,較晚的時間戳記會先撤消。以上例來說,執行rake db:rollback,會是20120116064707_oo.rb的撤消動作,再次執行rake db:rollback,才是20120116064658_xx.rb的撤消動作,撤消時也會從"schema_migrations"."version"刪除記錄的時間戳記。
再來看看遷移rb檔本身:
- *_create_messages.rb
class CreateMessages < ActiveRecord::Migration
def change
create_table :messages do |t|
t.string :name
t.text :content
t.boolean :is_read
t.timestamps
end
end
end
如果使用rails g model message...指定建立模型,則會建立CreateMessages類別,類別名稱與檔案名稱後半段必須有對應,類別名稱使用駝峰式(Camel case)命名,檔案名稱則以底線區隔單字命名,例如類別名稱為CreateMessages,檔案名稱就必須是YYYYMMDDHHMMSS_create_messages.rb,相對的,如果有個檔案名稱是YYYYMMDDHHMMSS_add_columns_to_messages.rb,則類別名稱必須取名為AddColumnsToMessages。Rails的遷移類別都繼承自ActiveRecord::Migration,首先看到change方法,從Rails 3.1開始,可逆轉的(Reversible)遷移方法,會定義在change方法中,執行rake db:migrate時,會呼叫change方法執行其中定義的內容,而執行rake db:rollback時,會自動根據change方法中的邏輯,執行逆轉的遷移方法。以上例而言,change方法中使用了create_table指定建立了messages表格,執行rake db:rollback時,會自動執行drop_table方法將messages表格刪除。可逆轉指令可參考ActiveRecord::Migration::CommandRecorder的文件說明,如果在執行撤消時指令無法逆轉,會丟出ActiveRecord::IrreversibleMigration。
若想分別控制rake db:migrate與rake db:rollback時執行的動作,可以自行定義up與down方法。建立遷移檔案時,Rails會嘗試判斷是否想執行可逆轉動作,如果是的話,遷移檔案中會自動產生change方法,如果Rails無法判斷是否想執行可逆轉方法,則會自動產生up與down方法。例如,可使用以下指令產生遷移檔案:
invoke active_record
create db/migrate/20120116082733_add_columns_to_messages.rb
因為add_column為可逆轉方法add_column的名稱,產生的遷移檔案會是:
- *_add_columns_to_messages.rb
class AddColumnsToMessages < ActiveRecord::Migration
def change
end
end
如果是以下指令:
invoke active_record
create db/migrate/20120116082852_a.rb
產生的遷移檔案會是:
- *_a.rb
class A < ActiveRecord::Migration
def up
end
def down
end
end
當然,無論是哪個,你都可以自由地在類別中選擇定義change、up或down,如果同時定義了change、up與down,則up與down會被忽略。up是執行rake db:migrate時要進行的動作,down是執行rake db:rollback會進行的動作。一個範例如下:
- *_add_columns_to_messages.rb
class AddColumnsToMessages < ActiveRecord::Migration
def up
add_column :messages, :from, :string
end
def down
remove_column :messages, :from
end
end
接著執行rake db:migrate,就會執行up方法,也就在messages表格中附加from欄位,如果執行rake db:rollback,則會執行down方法,也就會將messages表格中的from欄位移除。
Rails的遷移類別都繼承自ActiveRecord::Migration,可查看文件瞭解有哪些資料表建立、操作的方法,常見的有:
- create_table(name, options)
- drop_table(name)
- rename_table(old_name, new_name)
- add_column(table_name, column_name, type, options)
- rename_column(table_name, column_name, new_column_name)
- change_column(table_name, column_name, type, options)
- remove_column(table_name, column_names)
- add_index(table_name, column_names, options)
- remove_index(table_name, :column => column_name)
- remove_index(table_name, :name => index_name)
相關方法細節還可以參考ActiveRecord::ConnectionAdapters::SchemaStatements、ActiveRecord::ConnectionAdapters::TableDefinition與ActiveRecord::ConnectionAdapters::Table的文件說明。
Rails內建了一些欄位型態,例如基本的型態有:
- :primary_key:主鍵欄位
- :string:有限長度文字
- :text:無限長度文字
- :integer:整數
- :float:浮點數
- :decimal:十進位數
- :datetime:日期時間
- :timestamp:時間戳記
- :time:時間
- :date:日期
- :binary:二進位資料
- :boolean:外部鍵參考
視底層使用的資料庫而定,Rails內建的欄位型態是中性的,會自動轉換為底層資料庫合適的欄位型態,欄位型態說明,可以參考ActiveRecord::ConnectionAdapters::TableDefinition的 column(name, type, options = {}) 文件說明,在操作資料表格時,有些物件上會有些方法名稱與Rails內建欄位型態名稱相同,例如:
create_table :messages do |t|
t.string :name
t.text :content
t.boolean :is_read
t.timestamps
end
上例中t實際上會參考ActiveRecord::ConnectionAdapters::Table實例,t.string :name為messages表格建立name欄位,型態為有限長度文字,t.text、t.boolean的作用類推,有些方法會有預設的欄位建立動作,像是t.timestamps會自動建立created_at與updated_at兩個欄位,有些名稱是Rails實用的欄位名稱,像是id作為主鍵欄位、表格名稱_id作為外部鍵欄位、created_at、updated_at等。
在執行資料庫遷移時,除了rake db:migrate、rake db:rollback指令之外,還有更多細部控制遷移的指令,這在Rails官方文件 Migrations 中有詳細的介紹,其中還有更多資料庫遷移的細節可供參考。