LoginSignup
47
34

More than 5 years have passed since last update.

Railsからカラム削除するときにも型を書くと幸せになれるというお話

Posted at

Railsのマイグレーションファイルでカラム追加ってどう書きましたっけ。

class AddHeightToHumans < ActiveRecord::Migration
  def change
    add_column :humans, :height, :integer
  end
end

そうそう、こんなんでした。
では時が流れ、諸事情あってこのカラムは用済みですというお話になったとして、カラム削除はどう書きましょう?

class RemoveHeightToHumans < ActiveRecord::Migration
  def change
    remove_column :humans, :height
  end
end

うろ覚えでWebのサンプル引っ張ってくるとこんな風に書くと思いますがちょっと待った。
カラム削除のマイグレーションをdb:rollbackしようとしてエラーが出たことありませんか?
こんな感じになると思います(実際のログをもとに改変,DBはpostgres)。

$ bundle exec rake db:migrate:rollback STEP=1
D, [2015-12-09T15:40:00.307159 #18054] DEBUG -- :   ActiveRecord::SchemaMigration Load (25.9ms)  SELECT "schema_migrations".* FROM "schema_migrations"
D, [2015-12-09T15:40:00.463394 #18054] DEBUG -- :   ActiveRecord::SchemaMigration Load (0.6ms)  SELECT "schema_migrations".* FROM "schema_migrations"
I, [2015-12-09T15:40:00.478230 #18054]  INFO -- : Migrating to RemoveHeightToHumans (201512XXXXXXXX)
D, [2015-12-09T15:40:00.493499 #18054] DEBUG -- :    (0.3ms)  BEGIN
== 20151208031040 RemoveHeightToHumans: reverting ===============================
D, [2015-12-09T15:40:00.512146 #18054] DEBUG -- :    (0.2ms)  ROLLBACK
rake aborted!
StandardError: An error has occurred, this and all later migrations canceled:



remove_column is only reversible if given a type.

remove_columnに型がないって怒られてしまいます。
逆に言えば型を書けばロールバックできるんですね。
上はログをまとめやすく、実際のプロジェクトの名称が出てこないように恣意的に取り出していますが、実際にはなんとなくエラーの文面を流していた人もいるのではないでしょうか(私の場合はreversibleのあたりで思考停止して「なんかダメっぽいぞ」と反射で対処してしまってました。脳筋…)
特に開発中にブランチによってカラムが違ったりしてマイグレーションで増減させるときにこの辺りで刺さるとつらいことになります。

きちんと型指定してあげればchangeメソッドをわざわざupとdownに書きなおして記述するパターンやremove_columnをコメントアウトしてロールバックしてから手でカラム追加とかいう邪法ともおさらばです。

メソッド定義も一応チェックしておきます。

remove_column(table_name, column_name, type = nil, options = {}) public

モロに引数にtypeってありますね。

で、辿り着いたのが型を指定したこちら。
ぶっちゃけ、rails g migration RemoveHeightToHumans heightってやれば最初からこの形式で自動生成してくれますが、コマンドでのカラム指定忘れて手で書いちゃうとこれに辿りつけないことがあります。たぶん私だけじゃないはず。

class RemoveHeightToHumans < ActiveRecord::Migration
  def change
    remove_column :humans, :height, :integer
  end
end

ちなみにremove_columnsは型指定できないのでこのやり方はできません。

コマンド叩いたらこんな感じです(実際の出力をもとに改変)。

$ bundle exec rake db:rollback 
== 201512XXXXXXXX RemoveHeightToHumans: reverting ===============================
-- add_column(:humans, :height, :integer, {:default=>false})
   -> 0.0813s
== 201512XXXXXXXX RemoveHeightToHumans: reverted (0.0815s) ======================

できた

remove_indexも同じ要領で、nameだけ書いても消せるけどrollbackの時に戻せなくなるので、関係するカラムを書いてあげましょう。
もっと言うならschema.rbのindex定義をコピペしてadd_indexをremove_indexに変えるだけでOK。

「基本すぎない?」と突っ込まれそうで怖いのですが、私の観測範囲での感触では削除時に型指定できてないマイグレーションファイルまだまだあるんじゃないかなあ、という認識です。
普段はあまり触らないために忘れがちになるところでもあり、なまじ型指定しなくても動いてしまうのですが、折を見て思い出してやってください。

47
34
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
47
34