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?

ActiveStorageでhas_many_attachedにdependent: :destroyと同様の挙動を実現する方法(S3対応可)

Posted at

ActiveStorageを使ったアプリケーションで、複数ファイルを添付できるhas_many_attacheddependent: :destroyを設定したい場合があります。特にS3などの外部ストレージを使っていると、不要なファイルや関連情報が残らないように管理することが重要です。

この記事では、dependent: :destroyを設定できない理由と、それを補完する方法を紹介します。


問題: has_many_attachedではdependent: :destroyを設定できない

has_many_attachedは、ActiveStorageが内部でactive_storage_attachmentsテーブルを通じてモデルとファイルを関連付けています。この仕組みは通常のActiveRecordの関連付けとは異なるため、dependent: :destroyを直接設定することはできません。

また、S3のようなリモートストレージを使っている場合、ストレージ上の実ファイルも適切に削除する必要があります。


解決策: コールバックを使う

dependent: :destroyが使えない代わりに、モデルのbefore_destroyafter_destroyコールバックを利用して、関連付けられたファイルとそのメタデータ(active_storage_blobsactive_storage_attachments)を削除できます。


1. コールバックを使ったファイル削除

以下のように、before_destroyコールバックでファイルを削除します。

class Document < ApplicationRecord
  has_many_attached :files

  before_destroy :purge_attached_files

  private

  # ファイルとその関連情報を削除
  def purge_attached_files
    files.each(&:purge_later) # 非同期で削除
  end
end

ポイント

  • purge_later: ファイルを非同期で削除します(推奨)。S3などのリモートストレージを使用している場合でもパフォーマンスに優れています。
  • purge: 同期的に削除しますが、大量のファイル削除時には時間がかかる可能性があります。

削除される内容

  • ストレージ上の実ファイル。
  • active_storage_attachmentsの関連情報。
  • active_storage_blobsのメタデータ。

2. ActiveStorageの関連情報だけを削除

ファイル自体を削除せず、ActiveStorageの関連情報(active_storage_attachments)だけを削除したい場合は、以下のように実装します。

class Document < ApplicationRecord
  has_many_attached :files

  before_destroy :remove_active_storage_attachments

  private

  # ActiveStorageの関連情報を削除
  def remove_active_storage_attachments
    files.attachments.each(&:destroy) # attachments情報のみ削除
  end
end

この方法では、active_storage_blobsや実際のファイルは削除されないため、手動でクリーニングが必要です。


3. ActiveStorageの残データを定期的に削除

万が一、削除漏れが発生した場合に備えて、ActiveStorageが提供する以下のタスクでデータをクリーンアップできます。

rails active_storage:clean              # 未使用のファイルを削除
rails active_storage:purge_unattached   # 関連付けられていないBlobsを削除

これらをCronやスケジュールジョブ(Sidekiqなど)で定期実行すると、ストレージを効率的に管理できます。


まとめ

has_many_attachedではdependent: :destroyを直接使用できませんが、以下の方法で同様の動作を実現できます。

  1. コールバックでpurge_laterを使う(推奨)
    • ファイル本体と関連データを削除。
  2. attachments.each(&:destroy)で関連データのみ削除
    • ファイル本体は削除せず、柔軟なカスタマイズが可能。
  3. 定期的にクリーンアップタスクを実行
    • 削除漏れやストレージの膨張を防止。

用途や要件に応じて、適切な方法を選択してください。特にS3のような外部ストレージを使用している場合は、purge_laterを活用するのがおすすめです。

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?