LoginSignup
9
8

More than 3 years have passed since last update.

Rails - Discardを使って論理削除を実装する方法

Last updated at Posted at 2019-09-08

Discardを使って論理削除を実装する方法

現在開発しているプロジェクトで論理削除を実装する必要があった為、discardというgemを利用して実装しました。

現在論理削除のgemにはparanoiaとDiscardというgemがあります。当初スターの数などでparanoiaを利用しとうかと検討したのですが、rails公式の非推奨やactive recordのコア部分を上書きしてしまう実装の為、今後のRailsバージョンアップの際や他のgemとのコンフリクトを懸念があり、discardを利用する事になりました。

discard -> https://github.com/jhawthorn/discard
paranoia -> https://github.com/rubysherpas/paranoia

実装に掛かった時間は30分程度で、非常に使いやすいgemでおすすめです。

プロジェクトの解説

SurveyItemSurveyItemOptionというモデルがあり、それぞれSurveyという親モデルにbelongs_toしています。

Gemfile

gem 'discard', '~> 1.0'

Migration

論理削除が必要なモデルにdatetimeタイプのdiscarded_atというフィールドを追加します。論理削除する際、model.discardというメソッドを呼ぶことでdiscarded_atに論理削除した時間が入ります。元に戻す場合はmodel.undiscardでフィールドをnilにする事が出来ます。

  def up
    add_column :survey_items, :discarded_at, :datetime, after: :deleted
    add_index :survey_items, :discarded_at

    add_column :survey_item_options, :discarded_at, :datetime, after: :deleted
    add_index :survey_item_options, :discarded_at
  end

  def down
    remove_column :survey_items, :discarded_at
    remove_column :survey_item_options, :discarded_at
  end

Model

discard gemを使うためinclude Discard::Modelをモデルincludeします。
論理削除されていないレコードだけをデフォルトで返すようにしたい為、default_scope -> { kept }を追加します。

class SurveyItem < ApplicationRecord
   include Discard::Model
   default_scope -> { kept }

   belongs_to :survey
end
class SurveyItemOption < ApplicationRecord
   include Discard::Model
   default_scope -> { kept }

   belongs_to :survey
end

実装はこれだけ終わりです。

テスト

テストはminitestを使用しています。


  def test_discard
    assert_equal([
      survey_items(:survey_item_1),
    ].sort,
    SurveyItem.all.sort,
    "Should list all records")

    assert_equal([
      survey_items(:survey_item_1),
    ].sort,
    SurveyItem.kept.sort,
    "Should list all records")

    survey_items(:survey_item_1).discard
    assert_equal([],
    SurveyItem.kept,
    "Should list all records")

    survey = surveys(:survey_1)
    assert_equal([],
      survey.survey_items,
      "Should have zero survey items")
  end

デフォルトで論理削除されていないレコードを返すようにしているため、親レコードを取得した際、論理削除された子レコードは除かれて取得されます。素敵!

project > survey = Survey.first
=> #<Survey id: 1, company_id: 6, name: "アンケート 1", deleted: false, created_at: "2019-08-25 06:00:10", updated_at: "2019-08-25 06:00:10">

まだdiscardされていない。(まだ論理削除されていない)
project > survey.survey_items.first
=> #<SurveyItem id: 1, survey_id: 1, question_order: 1, question_name: "test", question_text: nil, question_type: nil, answer_data_type: nil, deleted: false, discarded_at: nil, created_at: "2019-09-08 11:08:57", updated_at: "2019-09-08 11:15:22">

discardする(論理削除する)
project > survey.survey_items.first.discard
=> true

project > survey = Survey.first
=> #<Survey id: 1, company_id: 6, name: "アンケート 1", deleted: false, created_at: "2019-08-25 06:00:10", updated_at: "2019-08-25 06:00:10">

survey_itemsは空で返ってくる。
project > survey = survey.survey_items
=> #<ActiveRecord::Associations::CollectionProxy []>

9
8
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
9
8