Soft delete records with Paranoia

How to use Paranoia to makes soft deleting records in Ruby on Rails
Luan Nguyen
Luan Nguyen
Oct 17, 2020 · 2 min read

What'll we learn:

  • Introduction to Paranoia gem
  • Installation
  • How to use Paranoia functionality?
  • Conclusion
Photo by @xavi_cabrera in Unsplash


Introduction

  • Paranoia is a gem that allow you to hide and restore records without actually deleting them and can recover them later if you want.
  • This gem re-implementation of ActsAsParanoid

Installation

Add paranoia gem to Gemfile:
gem 'paranoia', '~> 2.4'

Then run:
bundle install

Create migration to add soft-delete for Post model:
bin/rails generate migration AddDeletedAtToPost deleted_at:datetime:index

The migration will add column deleted_at  to Post model:
class AddDeletedAtToPost < ActiveRecord::Migration[6.0]
  def change
    add_column :posts, :deleted_at, :datetime
    add_index :posts, :deleted_at
  end
end

Run migration:
rails db:migrate

== 20201016152809 AddDeletedAtToPost: migrating ====================
-- add_column(:posts, :deleted_at, :datetime)
   -> 0.0333s
-- add_index(:posts, :deleted_at)
   -> 0.0270s
== 20201016152809 AddDeletedAtToPost: migrated (0.0605s) ===========


Usage

Enable this functionality for Post model:
class Post < ActiveRecord::Base
  acts_as_paranoid

  [...]
end

Paranoia will changes the default_scope of model, that mean Post.all will not include the deleted records.

Soft Delete a record:

When we destroy a record,  it'll set current timestamp to deleted_at  column
post.deleted_at
# => nil
post.destroy
# => post
post.deleted_at
# => [current timestamp] Fri, 16 Oct 2020 15:39:19 UTC +00:00

Restore a record:
It'll update { deleted_at  => nil }
post.restore
or
Post.restore(id)

Restore a record and their dependently destroyed Associated records:
post.restore(:recursive => true)
or
Post.restore(id, :recursive => true)

Find a deleted post
Post.find(id)
# => ActiveRecord::RecordNotFound
Post.unscoped.find(id) || Post.with_deleted.find(id)
# => post

Filtering:

Find all records, includes deleted posts:
Post.with_deleted

Find records, exclude deleted posts:
Post.without_deleted

Find only the deleted posts:
Post.only_deleted

Really Delete a Post
If you really want it gone, call really_destroy!:
post.really_destroy!

Conclusion

In this article, I already guided about how to soft-delete records instead of permanent-delete records in database. Paranoia actually is a useful gem, that makes soft deleting easier.