4
0

More than 3 years have passed since last update.

paranoia only_deletedメソッドのクセ

Last updated at Posted at 2021-01-25

何が言いたいか

paranoiaが用意しているonly_deletedメソッドは便利そうに見えてクセがあるなと感じました。

paranoia?

https://github.com/rubysherpas/paranoia
論理削除でおなじみのgem。
acts_as_paranoidとモデルに書くだけで、物理ではなく論理削除してくれるようになる。
(destroy!とかするとdeleted_atに値が入る)

事象

下記のようなテーブルがあるとします。
Userモデル

id name age deleted_at
1 けんじ 5 2021-01-01 11:38:30
2 たろう 6 null
3 じろう 5 null

今回登場するメソッド

with_deleted

論理削除されたレコードも取得できる
便利そう!

User.all
# => ID: 2, 3を取得できる
User.with_deleted
# => ID: 1, 2, 3を取得できる

only_deleted

論理削除されたレコードのみ取得できる
これも便利そう!

users = User.with_deleted
# => ID: 1, 2, 3のレコードを取得できる

users.only_deleted
# => ID: 1のみ取得できる

何がクセ?

5歳児のレコードをusersに代入

users = User.where(age: 5)
p users
# => <ActiveRecord::Relation [#<User id: 3, name: "じろう", age: 5, deleted_at: nil>]>

それにonly_deletedしてみる

p users.only_deleted
# => <ActiveRecord::Relation [#<User id: 1, name: "けんじ", age: 5, deleted_at: 2021-01-01 11:38:30>]>

id: 1のけんじくんのレコードがとれている・・・。
usersに論理削除されたレコードは無いので、てっきり何も返ってこないものと思いました。
where.not(deleted_at: nil)した時とは挙動が異なるようですね。

ソースを見に行く

    def only_deleted
      if paranoia_sentinel_value.nil?
        return with_deleted.where.not(paranoia_column => paranoia_sentinel_value)
      end
      # if paranoia_sentinel_value is not null, then it is possible that
      # some deleted rows will hold a null value in the paranoia column
      # these will not match != sentinel value because "NULL != value" is
      # NULL under the sql standard
      # Scoping with the table_name is mandatory to avoid ambiguous errors when joining tables.
      scoped_quoted_paranoia_column = "#{self.table_name}.#{connection.quote_column_name(paranoia_column)}"
      with_deleted.where("#{scoped_quoted_paranoia_column} IS NULL OR #{scoped_quoted_paranoia_column} != ?", paranoia_sentinel_value)
    end
    alias_method :deleted, :only_deleted

なんか最初にwith_deletedしている。。
なるほど、only_deletedとは、

users = users.with_deleted
# => ID: 1, 3を取得
users.where.not(deleted_at: nil)
# => ID: 1を取得

をしているというわけですね。

あとがき

単純に論理削除されたレコードのみ取得するときはwhere.not(deleted_at: nil)するのもありだと思いました。
「便利ー!」と思ってonly_deletedを使うと、場合によっては意図しない論理削除されたレコードを拾ってくる可能性がありそうだなと。
(ActiveRecordを取得するスコープが意図したものしか取得しない保証があるならOKだと思います。)

あとdeletedというエイリアスがあったの初めて知りました。

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