何が言いたいか
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
というエイリアスがあったの初めて知りました。