deviseで論理削除を実装する必要があり、deviseのWikiはあるけど日本語の記事が見当たらなかったので当該記事を翻訳することにしました。
参考:How to: Soft delete a user when user deletes account
物理削除と論理削除の違い
普通にDBからデータを削除することを物理削除と言います。一方で、DBにフラグとなるフィールドを作成し、削除時に削除フラグを立てることにより仮想的に削除時に見えなくする処理のこと。
deviseのデフォルトの退会機能は物理削除だが、例えば退会後もユーザー情報を保持したい場合は論理削除を実装する必要があります。(というか、多分そういう場合がほとんどではないかな)
以下、訳文。
ユーザーアカウントを削除するがユーザー情報は保持する(論理削除)
ユーザーがアカウントの削除を選択すると、すべてのデータが工夫によって破壊されます。
ユーザーデータは保持する一方で、ユーザーを非アクティブにし、ログインできないようにすることが求められる場合があります。
以下はユーザーを論理削除する方法です。
- datetime型の
deleted_at
カラムを追加 - routingの
users/registrations#destroy
をオーバーライド - registrations controllerの
users/registrations#destroy
をオーバーライド - 論理削除でユーザーモデルを更新し。認証でユーザーがアクティブかどうかを確認する
- delete時のカスタムメッセージを追加
1. Users
モデルにdeleted_at
を追加
下記をターミナルで実行
rails g migration AddDeletedAtColumnToUsers deleted_at:datetime
rake db:migrate
2. config/routes.rb
で削除時のroutingをオーバーライド
# devise_for :usersの部分を下記のように修正
devise_for :users, :controllers => { :registrations => 'users/registrations' }
3. registrations controllerのusers/registrations#destroy
をオーバーライド
まだ作成していなければ、app/controllers/users/registrations_controller.rb
を作成する。
ここでDevise registrations controllerを継承し、destroyをオーバーライドする。
# app/controllers/users/registrations_controller.rb
class Users::RegistrationsController < Devise::RegistrationsController
# DELETE /resource
def destroy
resource.soft_delete
Devise.sign_out_all_scopes ? sign_out : sign_out(resource_name)
set_flash_message :notice, :destroyed
yield resource if block_given?
respond_with_navigational(resource){ redirect_to after_sign_out_path_for(resource_name) }
end
end
4. 論理削除でユーザーモデルを更新し。認証でユーザーがアクティブかどうかを確認する
# app/models/user.rb
# 物理削除の代わりにユーザーの`deleted_at`をタイムスタンプで更新
def soft_delete
update_attribute(:deleted_at, Time.current)
end
# ユーザーのアカウントが有効であることを確認
def active_for_authentication?
super && !deleted_at
end
# 削除したユーザーにカスタムメッセージを追加します
def inactive_message
!deleted_at ? super : :deleted_account
end
5. 削除されたアカウントでログインしようとしたときのカスタムエラーメッセージを設定
# config/locales/*your-filename-here*.yml
en:
devise:
failure:
deleted_account: "You've deleted your account. Thanks!"
ja:
devise:
failure:
deleted_account: "このアカウントは既に削除されています。"
終わりに
以上で終了です。結構簡単に実装できますね。
でも論理削除だけだと、「削除したアカウントがまた同じメールアドレスで登録しようとするときはどうするの?」となってしまうので、その方法が無いか調べましたが見つかりませんでした。
多分同じようにコントローラーを継承してオーバーライドしていくんでしょうが、confirmation
などもあると厄介ですね。
もしご存知の方が居れば良い方法を教えて頂けるとありがたいです。