ActiveRecordでRDBのゴミ掃除を都度行う
開発時間が限られる個人開発だと、Railsの有り難みを感じる、この頃です。
さて、ActiveRecord
を使っているとdestroy
時に後片付けをする事が多いかと思います。
通常のhas_many
であれば標準の機能で事足りますが、それ以外にも、依存関係が消えた時に不要になったレコードを一緒に削除したい事はあるのではないでしょうか。

適当な例を挙げると、UserやGroup側にbelongs_to
、Image側にhas_many
があるような場合です。
この場合、使われなくなったImageを消す方法として、以下の2つの方法がパッと考えられます。
- 依存するレコード削除時に毎回調べる方法
- 定期的にバッチでゴミ掃除をする
今回は頻度が多くない想定なので前者で実装します。後者だとsidekiq-cron使ったりすれば良さそうです。
コード
after_commit do
reference_gc(:image)
end
# @param columns [Array<Symbol>]
def reference_gc(*columns)
columns.each do |column|
# belongs_to :image の情報を取得する
association = self.class.reflect_on_association(column)
next if association.nil?
# class (Image) を取得
reference_klass = association.klass
# 現在参照しているImage
current_reference_obj = method(column).call
# 更新されている場合, 変更前のImage
previous_reference_obj = reference_klass.find_by(
id: previous_changes[association.foreign_key]&.first
)
[current_reference_obj, previous_reference_obj].each do |reference_obj|
next if reference_obj.nil?
# Image側に書かれたhas_manyのプロパティ名を取得 (:group, :user)
property_names = reference_klass.reflect_on_all_associations(:has_many).map(&:name)
count = property_names.inject(0) { |sum, item| sum + reference_obj.method(item).call.count }
reference_obj.destroy! if count.zero?
end
end
end
最適化はしていませんが、こんな感じでしょうか。場合によってコード変更が必要そうですが。
コードは煮るなり焼くなりお好きにどうぞ。
記事が気に入ったらチップを送ることができます!
You can give me a cup of coffee :)
Kyash ID: soranoba
Amazon: Wish List
GitHub Sponsor: github.com/sponsors/soranoba
PayPal.Me: paypal.me/soranoba
(Updated: )