4
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Railsで削除フラグを使うのにparanoiaが使えた

4
Posted at

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を追加します。

Gemfile
gem 'paranoia'

以下のようにモデルを作成します。

app/models/sample.rb
class Sample < ApplicationRecord
  # tinyintの項目を論理削除用カラムとして指定
  acts_as_paranoid column: :delete_flg
end

以下がポイントです。
ここで「削除でない場合の値」を指定しておくことで既存データベースでも使えるようになります。

config/initializers/paranoia.rb
module Paranoia
  # 削除じゃない状態の値を上書き 元々の値: nil
  @@default_sentinel_value = false
end

ちなみにdeleted_atのカラム名のデフォルト値も変更しようと思ったのですが、
https://github.com/rubysherpas/paranoia/blob/core/lib/paranoia.rb#L244

paranoia/lib/paranoia.rb
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 値あり: 削除
app/models/sample.rb
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を入れようとして怒られる。

上記のように復元でエラーが発生してしまいます。
※このあたりもモンキーパッチで対応可能とは思いますが、あまり書き換えすぎるとバージョンアップに対応できなくなりそうで控えておきたい所です。

4
2
0

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

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?