Rails移行でnull可能列をnull不可に変更するにはどうすればよいですか?


188

以前の移行で日付列を作成し、それをnull可能に設定しました。これをnullにできないように変更したいと思います。そのデータベースにnull行があると仮定して、これを行うにはどうすればよいですか?それらが現在nullの場合、これらの列をTime.nowに設定しても問題ありません。

回答:


204

マイグレーションでそれを行う場合、おそらく次のようにすることができます。

# Make sure no null value exist
MyModel.where(date_column: nil).update_all(date_column: Time.now)

# Change the column to not allow null
change_column :my_models, :date_column, :datetime, null: false

1
これは私の開発データベースを破壊したためです。代わりに、次のような明示的なハッシュ構文を使用しますMyModel.update_all({:date_column => Time.now}, {:date_column => nil})。元のフォームのクエリでは、すべてのモデルにフィールドの値がありません。
dimitarvp

更新していただきありがとうございます。私がこの回答を書いたときはそうではなかったことを知っていますが、当時使用していたRubyまたはRoRのバージョンを思い出せません。
DanneManne 2012

1
この移行で「アップ」/「ダウン」方式を使用していますか、または移行で単純な変更方式を使用できますか?
EE33、2012

2
change(1)ための方法をので、この場合には適していないupdate_all方法で移動し、潜在的な復帰の両方で実行されます。これは最悪のことではないかもしれませんが、(2)移行では、復帰時に列がどのように変更されたかを知る方法がないためです。したがって、この場合のために、私はに固執するだろうupdown
DanneManne 2012

2
興味のある人のために、私の答えはこれを単一のステップで行う方法を示しています。
リック・スミス

167

Rails 4では、これはより優れた(DRYer)ソリューションです。

change_column_null :my_models, :date_column, false

NULLその列に値を持つレコードが存在しないことを確認するには、4番目のパラメーターを渡すことができます。これは、値を持つレコードに使用するデフォルト値NULLです。

change_column_null :my_models, :date_column, false, Time.now

4
これにより、テーブルにすでにnull値がある場合に問題が発生します。 私の答えを参照してください
リック・スミス

5
3.2でも利用可能です。値がnullであるデフォルトを設定するための4番目のパラメーターもあります。
toxaq 2014年

1
プラス1 change_column_null。しかし、上記のリック・スミスのコメントは非常に有効なケースを指摘しています。
0112 2014年

null値を更新するためのクエリを追加するために更新されました。4番目のパラメーター(デフォルト値)は、将来のレコードにもデフォルトを設定したい場合にのみ役立ちます。
mrbrdo 2014年

3
実際、Rails 4.2のドキュメントによれば、4番目のパラメータは将来のレコードのデフォルト値を設定しません。列のデフォルト。」
マイクフィッシャー

70

Rails 4(他のRails 4の回答には問題があります):

def change
  change_column_null(:users, :admin, false, <put a default value here> )
  # change_column(:users, :admin, :string, :default => "")
end

NULLを許可しないようにNULL値を含む列を変更すると、問題が発生します。これはまさに、開発セットアップで正常に機能し、LIVEプロダクションにデプロイしようとするとクラッシュするタイプのコードです。最初にNULL値を有効な値に変更してから、NULL 禁止する必要があります。の4番目の値change_column_nullは、まさにそれを行います。詳細については、ドキュメントを参照してください。

また、新しいオブジェクトを作成するたびにフィールドの値を指定する必要がないように、通常はフィールドにデフォルト値を設定することを好みます。それを行うためにコメントアウトしたコードも含めました。


3
Rails 4の場合、これはコメントアウトされたデフォルト設定を含め、最も正確で完全な答えのようです。
マイクフィッシャー

4
テーブルに新しい列を追加していて、nullの新しい値を挿入したいが、列にデフォルト値を追加したくない場合は、移行でこれを行うことができます。 add_column :users, :admin, :string次にchange_column_null(:admin, :string, false, "new_value_for_existing_records")
colsen

34

値を持つchange_columnステートメントを持つマイグレーションを作成し:default =>ます。

change_column :my_table, :my_column, :integer, :default => 0, :null => false

参照:change_column

データベースエンジンによっては、使用する必要がある場合があります change_column_null


1
これでうまくいきました。MySqlをローカルで使用する。Heroku(Postgres)でアプリをプッシュして実行すると、私がnullを書き込んでいたときにnullではなかった列にぶつかりました。「change_column_null」のみが機能し、MySqlで「change_column ...:null => false」を使用できませんでした。ありがとう。
rtfminc

1
そうchange_column_null後に移行するものだった
js111

1
PostgesはMySQLよりも厳密です-必要になると思いますchange_column_null
jessecurry

3
@rtfmincエッジケースに関する多くの問題を回避できるため、開発と運用で同じデータベースエンジンを使用することを強くお勧めします。
yagooar 2013年


3

レール4.02+に係るドキュメントのような方法がないupdate_all2つの引数を持ちます。代わりに、次のコードを使用できます。

# Make sure no null value exist
MyModel.where(date_column: nil).update_all(date_column: Time.now)

# Change the column to not allow null
change_column :my_models, :date_column, :datetime, null: false

2

既存のレコードがある場合は、add_timestampsとnull:falseを使用できないため、解決策は次のとおりです。

def change
  add_timestamps(:buttons, null: true)

  Button.find_each { |b| b.update(created_at: Time.zone.now, updated_at: Time.zone.now) }

  change_column_null(:buttons, :created_at, false)
  change_column_null(:buttons, :updated_at, false)
end
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.