はじめに
タイトルの環境にて、ユーザーの論理削除および一意制約の両立にかなり苦戦したので
解決方法を軽くまとめます。
deviseの一意制約をオーバーライドして独自の制約を持たせる方法などが出ていましたが
どれも面倒でもっと簡単にできないかと思い調べた結果です。
やりたいこと
先の環境にて(特にMySQL)、deviseデフォルトのユーザー物理削除ではなく論理削除を行い、
かつ退会したユーザーが同じアドレスやIDで再登録できるよう一意制約を担保したい。
PostgreSQL, SQLite
こちら2つのDBでは部分indexを活用することで簡単に実装できるそうです。
・実装記事
・add_index仕様1
・add_index仕様2
この、部分indexをMySQLは採用しておらず簡単に扱えない模様。
(情報が5.7ばかりだから8系では採用しているのか?どちらにしろRailsのmigrateからは無理そう)
軽くアンチMySQLになるかも…
結論(MySQLでの実現方法)
この記事みたらいけると思います。
https://vanhuyz.com/how-to-apply-unique-restriction-with-soft-delete-in-rails/
- deleted_atカラムを追加後、
- deleted_atにNullではなくアクティブユーザーにも特定の値をもたせることで、
2カラムでのUNIQUEを実現し(dleted_atがNULLだと実現しない) - シンプルに論理削除と一意制約を両立
最高に優良な記事をどうもありがとう!!
実装
[環境]
- Ruby:2.6.6p146
- Rails:6.0.3.3
- MySQL:8.0.21
- mysql2:0.5.3
- devise:4.7.2
- paranoia:2.4.2
準備
環境構築やdevise, paranoia導入等の基本部分の実装は他に沢山の記事が出ているので割愛します。
devise標準の会員登録・退会機能等が動作する前提です。
1. 設定ファイルparanoia.rbを作る
# ユーザー退会後の論理削除・一意制約を両立させるための処理
# 非削除レコードはdeleted_at = '0000-01-01 00:00:00'
# 削除済みレコードはdeleted_at != '0000-01-01 00:00:00'
Paranoia.default_sentinel_value = DateTime.new(0)
2. migrationでのindex変更
デフォルトのUsersテーブルのindexを変更するマイグレーションファイルを作成
rails g Change_Index_To_Users
作成したマイグレーションファイルを以下のように変更
一度デフォルトで作成されたemailインデックスを削除し
新たに[email, deleted_at]でのuniqueインデックスを作成
class ChangeIndexUniuqueToUsers < ActiveRecord::Migration[6.0]
def change
remove_index :users, :email
add_index :users, [:email,:deleted_at], unique: true
end
end
migration
rails db:migrate
3. Validation追加
アプリケーションレベルでのバリデーションを追加
class User < ActiveRecord::Base
acts_as_paranoia
validates :email, uniqueness: { scope: :deleted_at }
4. 動作確認
railsコンソールもしくはrailsサーバーを立ち上げ実際にユーザー作成、退会、同アドレスでの再登録を実施してDBを確認します。
無事同じアドレスでの再登録に成功しました!!
他何かいい方法などあれば是非教えてくださいませ~!