カテゴリー:
Rails
タグ:
 Rails delete_all

このエントリーをはてなブックマークに追加
更新日時:
2015年08月08日(土)
作成日時:
2015年08月08日(土)

前の記事 / 次の記事

振る舞いが違うってうかそもそも別のメソッドだから違って当然なんだけど、
同じメソッド名だったら同じことが起こりそうな気がするじゃん。
しなかったんだけど。

次のようなクラスを定義した時、

class Article < ActiveRecord::Base
  has_many :comments
end

class Comment < ActiveRecord::Base
  belongs_to :article
end

次の二つのdelete_allは同じ動作をする気がしたんだけど気のせいだった。
それぞれ結構異なる別々の振る舞いをする。

article = Article.find(params[:id])
Comment.delete_all(article_id: article.id)
article.comments.delete_all

前者のメソッドは ActiveRecord::Base#delete_all で、
条件に合致するレコードをテーブルから削除する。

後者のメソッドは ActiveRecord::Associations::CollectionProxy#delete_all で、
レシーバーとレシーバーの親レコードとのリレーションを切断する。

この時、親レコードの has_many に
dependent が指定されていないか、nullify が指定されていたら、
レシーバーの外部キーに nil をセットすることでリレーションを切断する。

delete_all または destroy が指定されていたら、
レシーバーを削除することでリレーションを切断する。

なので、後者の場合dependentに何も指定していない場合は、
デフォルトが nullify なので comment の atricle_id を nilにアップデートしにいく、削除はしない。
しかも nullify されるとリレーションは切断されるからからビュー上は消えたように見えるけど
nil がセットされるだけだからしっかり生きてる。というところで嵌まった。

後者のメソッドでnilをセットするのではなくレコードを削除して欲しい時は、
dependent に delete_all または destroy を指定するか delete_all を発行する時に delete_all を指定する。

class Article < ActiveRecord::Base
  has_many: comments, dependent: :delete_all
end

or

article = Article.find(params[:id])
article.comments.delete_all(:delete_all)

この時 dependent に destroy を指定していても、
delete_all が発行された時に実行されるのは
あくまでdelete_all であって destroy はされないので注意。

delete_all: コールバックが走らない
destroy: コールバックが走る

まあ、どちらのメソッドで削除すべきかじゃなくて、
文脈によって使い分けるものなんだろうと。