LoginSignup
2
3

More than 1 year has passed since last update.

Deviseで論理削除を実装する

Posted at

この記事の内容

DeviseでUser論理削除機能を実装する機会がありました。
Deviseを使うのが2年前のスクール卒以来...?というのもあり、機能の理解含めてなかなか勉強になったので、記事にして書き留めておこうと思います。
なお、実装時のバージョンは以下の通りです。

  • Rails 6.0.0
  • Devise 4.6

参考にした情報

Deviseの論理削除に関しては、公式のドキュメントに載っている他、それを翻訳した記事も存在します。

本記事では、上記に加えてユーザーのメールアドレスも変更していること、実装時にもらったアドバイスも含めている点に違いがあります。

仕様

今回は、ユーザーが「他のユーザー」を削除する機能で、ユーザーは自分自身を削除することはできません。
また、削除されたユーザーには、deleteフラグをつけるとともに、
メールアドレスを以下のように変更しようと思います。

hoge@example.com_deleted_削除された日時

削除された日時などをメールアドレスに追加するのは、万が一同じメールアドレスで再度登録があっても、DBの一意性制約に引っ掛からず登録できるようにするためです。

実装

公式ドキュメント等と同じところ

途中までは上記の参考記事と同じです。

userモデルにdeleted_atカラムを作ります。

rails g migration AddDeletedAtColumnToUsers deleted_at:datetime
rails db:migrate

今後、deleted_atカラムに値が入っていれば「削除済みユーザー」とみなします。

削除時のルーティングをオーバーライドします。

devise_for :users, :controllers => { :registrations => 'users/registrations' }

ここからがオリジナルポイント

users::registrations_controllerdestroyメソッドをオーバーライドします。実際に書いたコードはこんな感じです。

controllers/users/registration_controller.rb
class Users::RegistrationsController < Devise::RegistrationsController

  def destroy
    user = User.find(params[:id])
    soft_delete(user)
    set_flash_message :notice, :destroyed
    yield resource if block_given?
    respond_with_navigational { redirect_to root_path }
  end

  private

  def soft_delete(user)
    deleted_email = user.email + '_deleted_' + I18n.l(Time.current, format: :delete_flag)
    user.assign_attributes(email: deleted_email, deleted_at: Time.current)
    user.skip_reconfirmation!
    user.save!
  end
end

まず、今回は「画面上から自分以外の誰かを選択して、その人を削除する」仕様だったので、該当userのidをparamsで飛ばして変数userに代入しています。

soft_deleteの部分は、公式のドキュメントとだいぶ違くなりました。

まず、ユーザー自身に「削除」という振る舞いを持たせる仕様ではないので、モデルメソッドではなく、controllerのメソッドとして記載。

2021-07-18_20-10-44

の様に、秒まで記載してくれて、間に空欄のない日付形式をメールアドレスに足したかったので、
format: :delete_flagdeleteメソッド用の日付けフォーマットを定義。

最後に、deviseはユーザーのメールアドレスが変更されると確認メールを飛ばしてしまうので、

user.skip_reconfirmation!

で削除したユーザーに確認メールを飛ばすのをスキップしています。

安全・安心のために(削除したユーザーのログアウト)

さらに、削除したユーザーがログイン中の場合、そのユーザーを強制サインアウトさせられると安心です。
userモデルにて以下の様に設定しました。

models/user.rb
class User < ApplicationRecord
  def active_for_authentication?
    super && !deleted_at
  end
end

これは公式ドキュメントの記載の通りですactive_for_authentification?メソッドの理解に少し時間がかかったのでdeviseのドキュメントへのリンクも追記しておきます。

完成!

以上で、完成でした。
deviseを理解する初期に実装した機能だったので、当初はかなり複雑な様に思えたのですが、実際にはそうでもなかったですね^^
他にも、deviseは複雑な内容が多かったので、またノートを書きたいと思います。

2
3
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
2
3