0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Rails】Relation#delete_allとCollectionProxy#delete_allの違い

Posted at

はじめに

Ruby on Railsで複数のレコードを削除する際に使用されるメソッドとしてRelation#delete_allCollectionProxy#delete_allがあります。

この記事では両者の違いをまとめます。

Relation#delete_allCollectionProxy#delete_allの違い

まずは両者の基本的な違いを確認します。

Relation#delete_all

Relation#delete_allはActive RecordのRelationオブジェクトに対して使用されるメソッドです。

指定した条件に合致するレコードを一括で削除します。

SQLのDELETE文を直接発行するため非常に高速に動作しますが、削除時にモデルのコールバックやバリデーションは実行されません。

使用例

# ステータスが'inactive'のユーザーを一括削除
User.where(status: 'inactive').delete_all

CollectionProxy#delete_all

一方、CollectionProxy#delete_allはActive Recordの関連付けを通じてアクセスされるオブジェクトに対して使うメソッドです。

has_onehas_manyなどの関連づけられたコレクションに対して一括削除を行います。

使用例

# 投稿に紐づくコメントを削除
post = Post.first
post.comments.delete_all  # postsテーブルとcommentsテーブルの関連付けを通して削除

引数の取り扱いの違い

Relation#delete_allCollectionProxy#delete_allは引数の扱いが異なります。

実際のRailsのコードで確認します。

Relation#delete_all

Relation#delete_allは引数を受け取りません。

参考:Relation#delete_allの定義

activerecord/lib/active_record/relation.rb
def delete_all
  return 0 if @none

  invalid_methods = INVALID_METHODS_FOR_DELETE_ALL.select do |method|
    value = @values[method]
    method == :distinct ? value : value&.any?
  end
  if invalid_methods.any?
    raise ActiveRecordError.new("delete_all doesn't support #{invalid_methods.join(', ')}")
  end

  model.with_connection do |c|
    arel = eager_loading? ? apply_join_dependency.arel : build_arel(c)
    arel.source.left = table

    group_values_arel_columns = arel_columns(group_values.uniq)
    having_clause_ast = having_clause.ast unless having_clause.empty?
    key = if model.composite_primary_key?
      primary_key.map { |pk| table[pk] }
    else
      table[primary_key]
    end
    stmt = arel.compile_delete(key, having_clause_ast, group_values_arel_columns)

    c.delete(stmt, "#{model} Delete All").tap { reset }
  end
end

CollectionProxy#delete_all

CollectionProxy#delete_allは内部的に引数dependentオプションを受け取る実装となっています。

ただしユーザーが直接引数を渡すことは想定されておらず、あくまでもActive Record内部の処理用です。

関連付けを定義する際、以下のようにdependentオプションを付与するかと思います。

app/models/user.rb
class User < ApplicationRecord
  has_many :posts, dependent: :destroy  # dependentオプションを設定
end

ここで設定した内容に応じて、dependentオプションに引数が渡され、CollectionProxy#delete_allの挙動が変わるのです。

参考:CollectionProxy#delete_allの定義

activerecord/lib/active_record/relation.rb
def delete_all(dependent = nil)
  @association.delete_all(dependent).tap { reset_scope }
end

おわりに

  • Relation#delete_all: 引数を受け取らない
  • CollectionProxy#delete_all: Active Recordの内部で引数を受け取る

恥ずかしながら両者の違いを意識せず同じdelete_allメソッドとして使っていました。

今後はこういった、Railsの内部挙動にも注意を払っていきたいです。

参考

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

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?