この記事の内容
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_controller
のdestroy
メソッドをオーバーライドします。実際に書いたコードはこんな感じです。
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_flag
でdelete
メソッド用の日付けフォーマットを定義。
最後に、deviseはユーザーのメールアドレスが変更されると確認メールを飛ばしてしまうので、
user.skip_reconfirmation!
で削除したユーザーに確認メールを飛ばすのをスキップしています。
安全・安心のために(削除したユーザーのログアウト)
さらに、削除したユーザーがログイン中の場合、そのユーザーを強制サインアウトさせられると安心です。
user
モデルにて以下の様に設定しました。
class User < ApplicationRecord
def active_for_authentication?
super && !deleted_at
end
end
これは公式ドキュメントの記載の通りですactive_for_authentification?
メソッドの理解に少し時間がかかったのでdeviseのドキュメントへのリンクも追記しておきます。
完成!
以上で、完成でした。
devise
を理解する初期に実装した機能だったので、当初はかなり複雑な様に思えたのですが、実際にはそうでもなかったですね^^
他にも、deviseは複雑な内容が多かったので、またノートを書きたいと思います。