give IT a try

プログラミング、リモートワーク、田舎暮らし、音楽、etc.

Railsでカラムのデータ型を変更する場合の手順

はじめに

自分用のメモです。
Railsでカラムのデータ型を変更する場合の手順を載せておきます。
関連するインデックスやHeroku Schedulerなどは人によっては使っていないと思いますが、そのあたりは臨機応変に読み替えてください。

実行環境

手順

概要

先に手順の概要をリストアップするとこんな感じです。
以前のエントリでも書きましたが、change_columnでいきなり既存のカラムのデータ型を変更しようとするとPostgreSQLでエラーが発生するので、ちょっと面倒な手順(新しいカラムを追加して既存のカラムとスワップ)になります。

  1. 設計する
  2. Gitでブランチを作る
  3. 新しいカラムの追加
    1. DB上で関連するインデックスを削除する
    2. DB上で古いカラムをリネームする
    3. DB上で新しいカラムを作成する
    4. DB上で新しいインデックスを作成する
  4. データのコンバートとコードの修正
    1. Modelにリネームした古いカラムの属性を追加する
    2. 古いカラムから新しいカラムへデータを変換するrakeタスクを作成する
    3. 必要に応じてコードを修正する
    4. テストがパスすることを確認する
    5. 必要に応じてその他の動作確認をする
    6. 問題がなければコードをコミットする
  5. ステージング環境への反映
    1. HerokuのSchedulerを停止する
    2. Herokuのステージング環境へデプロイする
    3. Heroku上のデータを変換する
    4. ステージング環境で動作確認する
  6. 不用になったカラムの削除
    1. DB上で古いカラムを削除する
    2. Modelから古いカラムの属性を削除する
    3. テストがパスすることを確認する
    4. 必要に応じてその他の動作確認をする
    5. 問題がなければコードをコミットする
  7. ステージング環境への反映(2回目)
    1. Herokuのステージング環境へデプロイする
    2. ステージング環境で動作確認する
    3. HerokuのSchedulerを再開する
  8. Gitで変更点をmasterにマージする

詳細

ここからが手順の詳細です。

1.設計する

どのカラムをどんなデータ型からどんなデータ型に変更するか検討します。
本当にその変更が必要かどうか、可能かどうかを検討します。
変更した場合のインパクトと修正箇所を検討します。


今回はstring型のカラムをinteger型に変更(修正)する場合を想定します。

  • テーブル名: users
  • 修正するカラム: worker_number (string => integer)
2.Gitでブランチを作る

適当な名前のブランチを作ります。

$ git checkout -b convert-worker-number-type
3.新しいカラムの追加

以下の変更は一回のMigrationファイル内で実施します。

  • DB上で関連するインデックスを削除する
  • DB上で古いカラムをリネームする
  • DB上で新しいカラムを作成する
  • DB上で新しいインデックスを作成する
$ rails g migration convert_worker_number_type_to_users
class ConvertWorkerNumberTypeToUsers < ActiveRecord::Migration
  def change
    remove_index :users, :worker_number
    rename_column :users, :worker_number, :worker_number_old
    add_column :users, :worker_number, :integer
    add_index :users, :worker_number, unique: true
  end
end


Migrationを実行します。

$ rake db:migrate
$ rake db:test:prepare
4.データのコンバートとコードの修正

4.1.Modelにリネームした古いカラムの属性を追加する
DB上で追加したworker_number_oldをModelにも追加します。

class User < ActiveRecord::Base
  attr_accessible :name, :worker_number, :worker_number_old # :worker_number_oldを追加
  validates :name, :worker_number, presence: true
  validates :worker_number, uniqueness: true
end


4.2.古いカラムから新しいカラムへデータをコピーするrakeタスクを作成する
タスク名やnamespace等は適当にどうぞ。

# lib/tasks/convert_worker_number.rake
namespace :seed do
  desc "Convert worker number for users"
  task :convert_worker_number => :environment do
    User.transaction do
      User.all.each do |item|
        item.worker_number = item.worker_number_old.to_i
        item.save!
      end
    end
  end
end


rakeタスクを実行します。

$ rake seed:convert_worker_number


4.3.必要に応じてコードを修正する
データ型の変更によって影響を受ける箇所があれば必要に応じて修正します。


4.4.テストがパスすることを確認する
以下はRSpecを素直に実行する場合です。

$ bundle exec rspec


4.5.必要に応じてその他の動作確認をする
念のため、実際にUIを操作してみた方が良いかもしれません。


4.6.問題がなければコードをコミットする
Herokuへのデプロイに向けて、いったんコミットします。

$ git add .
$ git commit -m 'converted worker_number type'
5.ステージング環境への反映

5.1.HerokuのSchedulerを停止する
思いがけないトラブル等を避けるためにSchedulerを停止します。


5.2.Herokuのステージング環境へデプロイする
今回はheroku_sanを使っているのでこんな感じです。

$ rake staging deploy


5.3.Heroku上のデータをコンバートする
Heroku上でもrakeタスクを実行して、既存データのコンバートを行います。

$ heroku run rake seed:convert_worker_number


5.4.ステージング環境で動作確認する
やはり念のため動作確認しておきましょう。

6.不用になったカラムの削除

いらないものは捨てて、コードやスキーマを清潔に保ちましょう。


6.1.DB上で古いカラムを削除する
カラムを削除するためのMigrationを作成します。

$ rails g migration remove_worker_number_old_from_users
class RemoveWorkerNumberOldFromUsers < ActiveRecord::Migration
  def change
    remove_column :users, :worker_number_old
  end
end


Migrationを実行します。

$ rake db:migrate
$ rake db:test:prepare


6.2.Modelから古いカラムの属性を削除する
Modelからworker_number_oldを削除します。

class User < ActiveRecord::Base
  attr_accessible :name, :worker_number # :worker_number_oldを削除
  validates :name, :worker_number, presence: true
  validates :worker_number, uniqueness: true
end


6.3.テストがパスすることを確認する
たぶん問題は起きないはず・・・ですが念のため。

$ bundle exec rspec


6.4.必要に応じてその他の動作確認をする
動作確認してください。


6.5.問題がなければコードをコミットする
次のデプロイに向けてコミットします。

$ git add .
$ git commit -m 'removed worker_number_old'
7.ステージング環境への反映(2回目)

古いカラムの削除をステージング環境にも反映させます。


7.1.Herokuのステージング環境へデプロイする
もう一回デプロイします。

$ rake staging deploy


7.2.ステージング環境で動作確認する
動作確認します。問題なければほぼ完了です。


7.3.HerokuのSchedulerを再開する
最後に停止していたタスクを再開します。
できればSchedulerも問題なく動くかどうか確認しましょう。

8.Gitで変更点をmasterにマージする

これで終わりです。必要に応じてGithub等にもPUSHします。

$ git checkout master
$ git merge convert-worker-number-type
$ git push


以上です。お疲れさまでした!!

あわせて読みたい

このエントリは過去に書いたこのエントリをもう少し詳しく書いた感じですね。
RailsのMigrationでカラムの型を変える時の注意点 - give IT a try