stringで保存されたデータをenumに変更します。
enumの設定
ゴールとなるenumの設定。
Reservation.rb
class Reservation < ApplicationRecord
enum status: { applied: 0, accepted: 1, cancelled: 2, completed: 3 }
end
migrationの設定
元データは、Reservation.status
にstringで'applied','accepted','cancelled','completed'
が入っています。
以下のようなマイグレーションで、カラム型をStringからIntegerに変更し、データも移行するところまで一括で行います。
class ReservationStatusConvertToEnum < ActiveRecord::Migration[6.0]
# データベース操作の際にコールバックを呼ばないよう、テーブルを使うだけのTmpモデルを作る
class TmpReservation < ApplicationRecord
self.table_name = :reservations
end
def change
# 元データのカラム名を変更
change_table :reservations do |t|
t.rename :status, :old_status
end
reversible do |dir|
dir.up do
# 新しくenum用のintegerカラムを追加
add_column :reservations, :status, :integer, null: false, default: 0
# テーブル情報をリセットする
TmpReservation.reset_column_information
# stringで持っていた名前をenumと同じ順でarrayにし、旧データをインデックス番号で取る
TmpReservation.find_each do |tr|
tr.status = %w(applied accepted cancelled completed).index(tr.old_status)
tr.save!
end
# 旧データを捨てる
remove_column :reservations, :old_status
end
# upの逆
dir.down do
add_column :reservations, :old_status, :string
TmpReservation.reset_column_information
TmpReservation.find_each do |tr|
r = Reservation.find(tr.id)
tr.old_status = r.status
tr.save!
end
remove_column :reservations, :status
end
end
end
end
参考: Rails Migration, converting from string to enum | stack overflow
Scopeの修正
条件をSQL形式で書いてしまっているところは、enumに変換後に期待するデータが取ってこれなくなりますので、修正します。
scope :applied_reservations, -> { where("status = 'applied'") } # NG
scope :applied_reservations, -> { where(status: 'applied') } # OK