Railsのテーブル設計から外れたデータベースを使う実装で使えたのでメモ。
論理削除のgemで有名なparanoaを使ってdeleted_atカラムを使って論理削除を実現できますが、
既存データベースでdelete_flgのような名前で論理削除をしているケースでもある程度対応できました。
※ただし、若干モンキーパッチ的な部分があり、本来のdeleted_atのような削除日時のカラムもある場合、共存が難しそうです。
検証環境
Rails 5.2
MySQL 5.7
検証
以下のテーブルがあるとします。
samples
| カラム名 | データ型 | 用途 |
|---|---|---|
| id | integer | |
| name | string | |
| delete_flg | tinyint | 0: 削除されていない 1: 削除 |
paranoiagemを追加します。
gem 'paranoia'
以下のようにモデルを作成します。
class Sample < ApplicationRecord
# tinyintの項目を論理削除用カラムとして指定
acts_as_paranoid column: :delete_flg
end
以下がポイントです。
ここで「削除でない場合の値」を指定しておくことで既存データベースでも使えるようになります。
module Paranoia
# 削除じゃない状態の値を上書き 元々の値: nil
@@default_sentinel_value = false
end
ちなみにdeleted_atのカラム名のデフォルト値も変更しようと思ったのですが、
https://github.com/rubysherpas/paranoia/blob/core/lib/paranoia.rb#L244
で
self.paranoia_column = (options[:column] || :deleted_at).to_s
のように直接記述されており、変更するのに骨が折れるので放置しました。
rails consoleで検証します。
# 作成して保存
irb(main):001:0> sample = Sample.new(name: 'test')
(0.9ms) SET NAMES utf8, @@SESSION.sql_mode = CONCAT(CONCAT(@@sql_mode, ',STRICT_ALL_TABLES'), ',NO_AUTO_VALUE_ON_ZERO'), @@SESSION.sql_auto_is_null = 0, @@SESSION.wait_timeout = 2147483
=> #<Sample id: nil, name: "test", delete_flg: false, created_at: nil, updated_at: nil>
irb(main):002:0> sample.save
(0.6ms) BEGIN
Sample Create (0.5ms) INSERT INTO `samples` (`name`, `delete_flg`, `created_at`, `updated_at`) VALUES ('test', FALSE, '2019-08-13 04:13:40', '2019-08-13 04:13:40')
(2.7ms) COMMIT
=> true
# 削除フラグはFALSEで保存される。
# 削除
irb(main):003:0> sample.delete
Sample Update (4.3ms) UPDATE `samples` SET `delete_flg` = TRUE, `updated_at` = '2019-08-13 04:13:48' WHERE `id` = 1
=> #<Sample id: 1, name: "test", delete_flg: true, created_at: "2019-08-13 04:13:40", updated_at: "2019-08-13 04:13:48">
# 削除フラグにtrueが入る
# 検索
irb(main):004:0> Sample.all
Sample Load (1.6ms) SELECT `samples`.* FROM `samples` WHERE `delete_flg` = FALSE LIMIT 11
=> #<ActiveRecord::Relation []>
# 削除フラグがfalseのものだけ検索
# 削除を戻す
irb(main):005:0> sample.restore
(0.5ms) BEGIN
Sample Update (0.7ms) UPDATE `samples` SET `delete_flg` = FALSE, `updated_at` = '2019-08-13 04:13:58' WHERE `id` = 1
(2.6ms) COMMIT
=> #<Sample id: 1, name: "test", delete_flg: false, created_at: "2019-08-13 04:13:40", updated_at: "2019-08-13 04:13:58">
# 削除フラグがFALSEに更新される
# 再び検索
irb(main):006:0> Sample.all
Sample Load (0.8ms) SELECT `samples`.* FROM `samples` WHERE `delete_flg` = FALSE LIMIT 11
=> #<ActiveRecord::Relation [#<Sample id: 1, name: "test", delete_flg: false, created_at: "2019-08-13 04:13:40", updated_at: "2019-08-13 04:13:58">]>
# 検索された
問題点
通常のdeleted_atを持つ以下のテーブルを用意
sample2
| カラム名 | データ型 | 用途 |
|---|---|---|
| id | integer | |
| name | string | |
| deleted_at | datetime | 値あり: 削除 |
class Sample2 < ApplicationRecord
# 通常通りに作成
acts_as_paranoid
end
rails consoleで実験
# 保存
irb(main):001:0> sample2 = Sample2.new(name: 'hoge')
(0.4ms) SET NAMES utf8, @@SESSION.sql_mode = CONCAT(CONCAT(@@sql_mode, ',STRICT_ALL_TABLES'), ',NO_AUTO_VALUE_ON_ZERO'), @@SESSION.sql_auto_is_null = 0, @@SESSION.wait_timeout = 2147483
=> #<Sample2 id: nil, name: "hoge", deleted_at: nil, created_at: nil, updated_at: nil>
irb(main):002:0> sample2.save
(0.6ms) BEGIN
Sample2 Create (0.6ms) INSERT INTO `sample2s` (`name`, `created_at`, `updated_at`) VALUES ('hoge', '2019-08-13 04:55:22', '2019-08-13 04:55:22')
(3.9ms) COMMIT
=> true
# 削除
irb(main):003:0> sample2.delete
Sample2 Update (2.5ms) UPDATE `sample2s` SET `deleted_at` = '2019-08-13 04:55:27', `updated_at` = '2019-08-13 04:55:27' WHERE `id` = 1
=> #<Sample2 id: 1, name: "hoge", deleted_at: "2019-08-13 04:55:27", created_at: "2019-08-13 04:55:22", updated_at: "2019-08-13 04:55:27">
# 復元
irb(main):004:0> sample2.restore
(1.2ms) BEGIN
Sample2 Update (1.1ms) UPDATE `sample2s` SET `deleted_at` = 0, `updated_at` = '2019-08-13 04:55:32' WHERE `id` = 1
(0.8ms) ROLLBACK
Traceback (most recent call last):
1: from (irb):4
ActiveRecord::StatementInvalid (Mysql2::Error: Incorrect datetime value: '0' for column 'deleted_at' at row 1: UPDATE `sample2s` SET `deleted_at` = FALSE, `updated_at` = '2019-08-13 04:55:32' WHERE `id` = 1)
# datetime型にfalseを入れようとして怒られる。
上記のように復元でエラーが発生してしまいます。
※このあたりもモンキーパッチで対応可能とは思いますが、あまり書き換えすぎるとバージョンアップに対応できなくなりそうで控えておきたい所です。