MigrationによるDB管理
Migrationとは
Migration とは、Railsで使うデータベースの管理機能で、テーブル作成・カラ ムの追加/変更などの作業を一元管理できます。SQL でスキーマを書くのでは なく、Rails独自の記法(Rubyの文法の範囲内)を使ってDB管理を行います。以 下のようなメリットがあります。
- スキーマのバージョン管理ができる
- rake コマンドでスキーマのバージョンアップ/ロールバックが可能
- データベースに依存しない書き方ができるので、他のデータベースに切り替えるのが容易
対応しているデータベース
現在、対応しているデータベースは MySQL, PostgreSQL, SQLite, SQL Server, Oracle です。
今後、対応DBは増えていくと思います。最新情報は、
http://api.rubyonrails.org/classes/ActiveRecord/Migration.html
の Database support のところを見てください。
また、ActiveRecord の各アダプタに migration に対応しているかの真偽を返 すメソッドがあります。
ActiveRecord::Base.connection.supports_migrations?
これが true なら migration 機能が使えます。
使い方
スキーマの各バージョン毎に Migration 定義ファイルを作成します。定義ファ イルは ActiveRecord::Migration を継承したクラスをひとつ含みます。この クラスに up と down というクラスメソッドを定義することにより、スキーマ のアップグレード/ダウングレードを行います。
generate migration
まず、最初のテーブル作成用の定義ファイルをつくってみます。以下のように して script/generate で migration 定義ファイルを新規に生成します。
% ruby script/generate migration InitialSchema
InitialSchemaの部分は任意の名前を指定でき、この部分が Migration 定義ファ イル内のクラス名になります(名前は initial_schema の形でも可)。定義ファ イルは以下のような内容で、db/migrate/001_initial_schema.rb のような名 前で生成されます。
class InitialSchema < ActiveRecord::Migration def self.up end def self.down end end
定義ファイルの編集
上記のファイルを編集し、アップグレード/ダウングレード時の処理を記述します。
self.up は、アップグレード時の処理です。ここでは、最初のテーブル作成時 の定義ファイルなので、主にスキーマ定義を行うことになります。たとえは以 下の記述が可能です。
- create_table(テーブル名, オプション)
- テーブルを作成する。ブロックの中でカラム定義ができる。
- drop_table(テーブル名)
- テーブルを削除する。
- rename_table(テーブル名, 新テーブル名)
- テーブル名を変更する。
- add_column(テーブル名, カラム名, 型, オプション)
- テーブルにカラムを追加する。
- remove_column(テーブル名, カラム名)
- カラムを削除する。
- rename_column(テーブル名, カラム名, 新カラム名)
- カラム名を変更する。
- add_index(テーブル名, カラム名)
- インデックスを追加する。
詳細は以下を参照してください。
http://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/SchemaStatements.html
では、例としてRailsでWikiクローンを作るのDBスキーマ定義をMigrationの機能で実現してみ ます。定義ファイルは以下のようになります。
class InitialSchema < ActiveRecord::Migration
def self.up
create_table(:pages) {|t|
t.column :name, :string, :null => false
t.column :title, :string
t.column :freezeflag, :boolean
t.column :updated_at, :datetime
t.column :content, :text
}
add_index :pages, :name
create_table(:confs) {|t|
t.column :site_name, :string, :null => false
t.column :author_name, :string
t.column :theme, :string
t.column :use_wikiname, :boolean
t.column :use_plugin, :boolean
}
Conf.create :site_name => 'hoge hoge', :author_name => '名無しさん',
:theme => 'hiki', :use_wikiname => true, :use_plugin => false
create_table(:users) {|t|
t.column :name, :string, :limit => 10
t.column :crypted_password, :string, :limit => 14
}
User.create :name => 'admin', :password => 'admin'
end
def self.down
drop_table :pages
drop_table :confs
drop_table :users
end
end
create_table でテーブル定義をします。引数でテーブル名とオプションを指 定できます(上記例ではオプションを指定していません。ほとんどの場合は不 要でしょう)。例では ActiveRecord にはつきものの id カラムがありません が、特に指定しない限り自動で付加されます。
create_table のブロック内でカラム定義を行います。
t.column :name, :string, :null => false
は、SQL で書くと
name varchar(255) NOT NULL
というような意味になります。この抽象表現で使用できる型と、実際に SQL でどのように変換されるかは、各DBアダプタの native_database_types で定 義されています。例えば sqlite ならば、activerecord の lib/active_record/connection_adapters/sqlite_adapter.rb というファイ ルの中にあります。
def native_database_types #:nodoc:
{
:primary_key => "INTEGER PRIMARY KEY NOT NULL",
:string => { :name => "varchar", :limit => 255 },
:text => { :name => "text" },
:integer => { :name => "integer" },
:float => { :name => "float" },
:datetime => { :name => "datetime" },
:timestamp => { :name => "datetime" },
:time => { :name => "datetime" },
:date => { :name => "date" },
:binary => { :name => "blob" },
:boolean => { :name => "boolean" }
}
end
その他、テーブル定義で使用できる命令についての詳細は、以下をみてください。
http://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/TableDefinition.html
テーブル定義以外にも、普通に ActiveRecord を使うときのように
User.create :name => 'admin', :password => 'admin'
と書いて、テーブル内にデータを挿入することもできます。
注意 ここでの ActiveRecord の操作は rails の app/models/*.rb ファイルの内容 に従います。上記の User の例では、テーブル定義に password というカラム はありませんが、モデル(app/models/users.rb)のほうで定義している (RailsでWikiクローンを作る10参照)のでこのような書き方が可能です。
self.down メソッド内では、ダウングレードするときの処理内容を定義します。 up と down を対で書いておくことにより、スキーマ定義を任意のバージョン に戻せるようになります。例では、テーブルを削除する drop_table を記述し ています。
database.yml の設定
config/database.yml を適切に設定します。migration は database.yml で指定 したデータベースに適用されます。
Migration実行
migrationを実行するには、RAILS_ROOT で以下のコマンドを実行します。
rake migrate
development 環境の現在のデータベースに対して、適用されていないすべての 定義ファイルが番号順に適用されます。どの番号まで適用されているかを覚え ておくためにDB内にschema_infoというテーブルが自動でつくられます。
また、定義を戻したい場合は
rake migrate VERSION=X
と番号を指定すればその番号まで戻ります。
rake migrate VERSION=0
とすると、一番最初の状態に戻ることになります。
また、デフォルトでは migrate は development 環境のデータベースに対して 実行されますが production 環境でやりたい場合は
rake RAILS_ENV=production migrate
のように実行します。
既存のDBからmigration定義ファイルを作るには
migrationという便利な機能があることを知らずに、すでに SQL で create table してDBスキーマを定義してしまった、という人に朗報です。
rails 環境では、以下のようにすると現在のデータベースの情報を読み込んで migration で使える形式のスキーマ定義ファイルを生成してくれます。
rake db_schema_dump
とすると db/schema.rb というファイルができあがります。たとえば RailsでWikiクローンを作る のデータベースがすでにある状態で、rake db_schema_dump する と以下の内容の db/schema.rb ファイルが生成されます。
# This file is autogenerated. Instead of editing this file, please use the
# migrations feature of ActiveRecord to incrementally modify your database, and
# then regenerate this schema definition.
ActiveRecord::Schema.define(:version => 1) do
create_table "confs", :force => true do |t|
t.column "site_name", :string, :null => false
t.column "author_name", :string
t.column "theme", :string
t.column "use_wikiname", :boolean
t.column "use_plugin", :boolean
end
create_table "pages", :force => true do |t|
t.column "name", :string, :null => false
t.column "title", :string
t.column "freezeflag", :boolean
t.column "updated_at", :datetime
t.column "content", :text
end
add_index "pages", ["name"], :name => "pages_name_index"
create_table "users", :force => true do |t|
t.column "name", :string, :limit => 10
t.column "crypted_password", :string, :limit => 14
end
end
これの create_table や add_index の部分を抜き取ってこれば migration 定 義ファイルは簡単に作成できるでしょう。前述の、手で作った定義ファイルと 若干表記は異なりますが、意味はだいたい同じです。ただし、以下の2点に注意。
- create_table に :force => true というオプションが指定されているため、 rake migrate すると既存のテーブルは drop される。
- 既存のテーブルの中身はコピーされない。初期値を insert したいなら手で追加する。
- self.down メソッドは手で書く
schema dump 機能を使えば、既存の DB も migration を使うやり方に移行し やすいと思います。

Keyword(s):
References:[FrontPage]