「このカラムdatetime
じゃなくてdate
でよさそう」
日付をあつかうからdatetime
にしてたけど、よくよく考えてみると「時間」は必要ないよねーとなったとき、date
に変換したくなりますよね。
今回のゴール
- Bookモデルは発売日
released_at(datetime)
をもっている - マイグレーションをして
released_on(date)
にしたい
環境
- Rails 6.0.3
- psql (PostgreSQL) 12.4
changeをつかったdatetime→dateの変換
def change
rename_column :books, :released_at, :released_on
change_column :books, :released_on, 'date USING CAST(released_on AS date)'
end
db:rollback
できますか?
これでよしよし、git commi..
、と、ちょっとまってください!
マイグレーションは巻き戻る(rollback)ことを考慮しておきましょう。
前述のマイグレーションをrollbackできるかを確かめてみましょう。
$ rails db:rollback
== ..... : reverting ========
rails aborted!
StandardError: An error has occurred, this and all later migrations canceled:
This migration uses change_column, which is not automatically reversible.
To make the migration reversible you can either:
1. Define #up and #down methods in place of the #change method.
2. Use the #reversible method to define reversible behavior.
なにがエラーになったのか
-
change_column
でロールバックしたとき、以前の型情報がわからないから元に戻せないから
'date USING CAST(released_on AS date)'
で型を変換できたものの、**「変換前の型の情報」**が抜け落ちてしまったため、ロールバックに失敗してしまいました。
これを解決するためのひとつの方法としてup/downをつかった方法に書きかえてみましょう。
up/downをつかった方法
def up
rename_column :books, :published_at, :release_on
change_column :books, :release_on, 'date USING CAST(release_on AS date)'
end
def down
rename_column :books, :release_on, :published_at
change_column :books, :published_at, 'timestamp USING CAST(published_at AS timestamp)'
end
こうすることによって、
-
db:migrate
ではup
のdatetime -> date
が実行される -
db:rollback
ではdown
のdate -> datetime
が実行される
おまけ:PostgreSQLでdatetime USING CAST
はエラーになる
change_column :books, :released_at, 'datetime USING CAST(released_at AS datetime)'
PG::UndefinedObject: ERROR: type "datetime" does not exist
LINE 1: ...released_at" TYPE datetime USING CAST(published_at AS datetime)
Rails世界のdatetime
は、PostgreSQL世界ではtimestamp
になるという話でした