🔽

Railsアプリで後からdeviseを導入したときのマイグレーションファイルをdownにしたい

2024/02/21に公開

はじめに

Railsアプリでdeviseを後から導入したときのことをまとめます。
初めての記事ですので、間違っている点などあればコメントを頂ければありがたいです。

前提

  • ruby 3.1.2
  • Rails 6.1.7.6
  • devise 4.9.3

deviseの導入は済んでいるものとして話を進めます。

Userモデルとテーブルの作成

以下のコマンドをターミナルで実行してUserモデルとテーブルを作成。
この時点ではdeviseは関係ありません

$ rails g model User name:string is_active:boolean
$ rails db:migrate



この時点で、スキーマファイルは以下のような形になります。

db/schema.rb
create_table "users", force: :cascade do |t|
    t.string "name"
    t.boolean "is_active"
    t.datetime "created_at", precision: 6, null: false
    t.datetime "updated_at", precision: 6, null: false
end

devise導入

以下のコマンドを実行するとdeviseを導入できます。

$ rails g devise User

このときUserモデルが既に存在している場合は、既存のUserモデル、テーブルに対してdeviseを導入してくれます。賢いですね。


作成されたマイグレーションファイルは以下になります。

db/migrate/20XXXXXXXXX_add_devise_to_users.rb
# frozen_string_literal: true

class AddDeviseToUsers < ActiveRecord::Migration[6.1]
  def self.up
    change_table :users do |t|
      ## Database authenticatable
      t.string :email,              null: false, default: ""
      t.string :encrypted_password, null: false, default: ""

      ## Recoverable
      t.string   :reset_password_token
      t.datetime :reset_password_sent_at

      ## Rememberable
      t.datetime :remember_created_at

      ## Trackable
      # t.integer  :sign_in_count, default: 0, null: false
      # t.datetime :current_sign_in_at
      # t.datetime :last_sign_in_at
      # t.string   :current_sign_in_ip
      # t.string   :last_sign_in_ip

      ## Confirmable
      # t.string   :confirmation_token
      # t.datetime :confirmed_at
      # t.datetime :confirmation_sent_at
      # t.string   :unconfirmed_email # Only if using reconfirmable

      ## Lockable
      # t.integer  :failed_attempts, default: 0, null: false # Only if lock strategy is :failed_attempts
      # t.string   :unlock_token # Only if unlock strategy is :email or :both
      # t.datetime :locked_at


      # Uncomment below if timestamps were not included in your original model.
      # t.timestamps null: false
    end

    add_index :users, :email,                unique: true
    add_index :users, :reset_password_token, unique: true
    # add_index :users, :confirmation_token,   unique: true
    # add_index :users, :unlock_token,         unique: true
  end

  def self.down
    # By default, we don't want to make any assumption about how to roll back a migration when your
    # model already existed. Please edit below which fields you would like to remove in this migration.
    raise ActiveRecord::IrreversibleMigration
  end
end

適宜このマイグレーションファイルを編集して、以下のコマンドを実行。

$ rails db:migrate



ここまでで一旦deviseの導入が完了です。
スキーマファイルを見ると、もともとのUserモデルに対して色々追加されているのが分かります。

db/schema.rb
create_table "users", force: :cascade do |t|
    t.string "name"
    t.boolean "is_active"
    t.datetime "created_at", precision: 6, null: false
    t.datetime "updated_at", precision: 6, null: false
    t.string "email", default: "", null: false
    t.string "encrypted_password", default: "", null: false
    t.string "reset_password_token"
    t.datetime "reset_password_sent_at"
    t.datetime "remember_created_at"
    t.index ["email"], name: "index_users_on_email", unique: true
    t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true
end

マイグレーションファイルをdownにしたい

ここで先程のdeviseのマイグレーションファイルに修正を加えたいとします。
マイグレーションファイルを書き換えるときはdownにする必要がありますね。
今回は直近のマイグレーションファイルなので以下のコマンドでdownにしようと試みます。

$ rails db:rollback

すると、以下のエラーが発生してうまくいきません。

StandardError: An error has occurred, this and all later migrations canceled:

ActiveRecord::IrreversibleMigration

何が起こっているのか?

先程のマイグレーションファイルを見てみると

db/migrate/20XXXXXXXXX_add_devise_to_users.rb
def self.down
    # By default, we don't want to make any assumption about how to roll back a migration when your
    # model already existed. Please edit below which fields you would like to remove in this migration.
    raise ActiveRecord::IrreversibleMigration
end

と、定義されています。これが原因でエラーが発生していました。
コメントをざっくり翻訳してみると、
「デフォルトでロールバックの方法は仮定したくないから、そっちで適宜編集してくれや」
的なことが書いてあります。
つまり、ここをうまく書き換えて上げればいいっぽいですね。

解決方法

今回はとりあえずself.upで変更される内容を打ち消すようなself.downを定義してみます。

db/migrate/20XXXXXXXXX_add_devise_to_users.rb
def self.down
    change_table :users do |t|
      # Database authenticatable
      t.remove :email, :encrypted_password

      # Recoverable
      t.remove :reset_password_token, :reset_password_sent_at

      # Rememberable
      t.remove :remember_created_at

      # Trackable
      # 以下のコメントアウトされたカラムは、もしself.upで有効にしたならば削除する
      # t.remove :sign_in_count, :current_sign_in_at, :last_sign_in_at, :current_sign_in_ip, :last_sign_in_ip

      # Confirmable
      # t.remove :confirmation_token, :confirmed_at, :confirmation_sent_at, :unconfirmed_email

      # Lockable
      # t.remove :failed_attempts, :unlock_token, :locked_at
    end
end



これで再度以下を実行してみましょう。

$ rails db:rollback

今度はエラーがなく実行できたと思います。


以下のコマンドを実行するとdownになっていることが確認できます。

$ rails db:migrate:status
database: db/development.sqlite3

 Status   Migration ID    Migration Name
--------------------------------------------------
   up     20xxxxxxxxxxxx  Create users
  down    20xxxxxxxxxxxx  Add devise to users

念の為スキーマファイルを見てみると、元に戻ってますね。

db/schema.rb
create_table "users", force: :cascade do |t|
    t.string "name"
    t.boolean "is_active"
    t.datetime "created_at", precision: 6, null: false
    t.datetime "updated_at", precision: 6, null: false
end

おわりに

今回はdeviseを後から導入した際にできるマイグレーションファイルをdownにする方法でした。
わざわざdownにしなくても新しいマイグレーションファイルを作って変更するという解決方法も
アリだと思います。どっちがいいんだろう?
繰り返しになりますが、まだまだ勉強中なので、間違っている点やもっといい解決方法等
があればぜひ教えて下さい!!

Discussion