今回は、Ruby on Railsのマイグレーションによるデータ移行についてお話します。
新しく入ったプロジェクトでマイグレーションを実行したら、エラーが起きてテーブルが作成できないことがありました。マイグレーションファイルを見たら、以下のようなコードが書かれており、マイグレーションによるデータ移行部分でエラーになっていました。
1
2
3
4
5
6
7
8
9
| class AddColumnToModels < ActiveRecord::Migration
def self.up
add_column :models, :column, :string
Model.named_scope.each do |model|
# モデルに関する様々な更新処理。実際はもっと複雑でした。
model.update_attributes!(:column => "example")
end
end
end |
エラーの原因は、マイグレーション内で利用しているモデルが存在しない、またはモデル内部のロジックがマイグレーション作成時と異なり例外が発生していることが原因でした。
マイグレーションによるデータ移行はよく行われます。
しかし、マイグレーションの中でモデルを利用してデータ移行をするのは避けるべきだと思います。
なぜなら、マイグレーション作成時のモデルと最新のモデルではロジックなどに差異がある可能性があり、それによってマイグレーションが動かなくなる可能性があるからです。また、モデルを修正するたびにマイグレーションファイルも修正するのも、保守性がよろしくありません。
そこで、私はデータ移行を行う場合、基本はモデルを使わずに直接SQLを書くようにしています。
1
2
3
4
5
6
| class AddColumnToModels < ActiveRecord::Migration
def self.up
add_column :models, :column, :string
execute "UPDATE models SET column = 'example'"
end
end |
こうすることでモデルとマイグレーションを切り離すことができます。また、モデルによるコールバックなどの無駄な処理がなくなることにより高速にデータ移行ができます。
とはいっても、モデルを利用した方が効率が良いときがあります。その場合は、マイグレーション専用のモデルを定義することで、通常のモデルと切り離して利用します。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| class AddColumnToModels < ActiveRecord::Migration
class TempModel < ActiveRecord::Base
set_table_name "models"
# データ移行に必要な処理だけを記載
end
def self.up
add_column :models, :column, :string
TempModel.named_scope.each do |model|
model.update_attributes!(:column => "value")
end
end
end |
私は上の2つを利用して、マイグレーション内のデータ移行を行っています。
モデルを使うデータ移行より手間はかかりますが、モデルやライブラリを修正した場合にマイグレーションを見直し修正する必要がなくなり、トータルでは手間が少なくなると思います。是非試してみてください。
製品戦略室チーフプログラマ
UGUISU(http://www.uguisu.biz)開発責任者