3
2

More than 3 years have passed since last update.

[Rails6]devise + paranoia + MySQL8系で実現するユーザー論理削除と一意制約の両立

Last updated at Posted at 2020-09-22

はじめに

タイトルの環境にて、ユーザーの論理削除および一意制約の両立にかなり苦戦したので
解決方法を軽くまとめます。
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/
1. deleted_atカラムを追加後、
2. deleted_atにNullではなくアクティブユーザーにも特定の値をもたせることで、
 2カラムでのUNIQUEを実現し(dleted_atがNULLだと実現しない)
3. シンプルに論理削除と一意制約を両立

最高に優良な記事をどうもありがとう!!

実装

[環境]
- 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を作る

config/initializers/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インデックスを作成

db/migrate/202009220000000.rb
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追加

アプリケーションレベルでのバリデーションを追加

app/models/user.rb
class User < ActiveRecord::Base
  acts_as_paranoia
  validates :email, uniqueness: { scope: :deleted_at }  

4. 動作確認

railsコンソールもしくはrailsサーバーを立ち上げ実際にユーザー作成、退会、同アドレスでの再登録を実施してDBを確認します。
論理削除_一意制約.PNG

無事同じアドレスでの再登録に成功しました!!
他何かいい方法などあれば是非教えてくださいませ~!

3
2
1

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