Active Storageを使ってアップロードファイルの管理をしていると、管理するテーブル (active_storage_blobs
, active_storage_attachments
) とストレージに実際に保存されているファイル (実体ファイル) との間に、何らかの理由で不整合が起きることがある。
中でも active_storage_attachments
だけが削除され、それに紐づく active_storage_blobs
と実体ファイルが残ってしまっている場合には、 ActiveStorage::Blob.unattached
というスコープが使える。
❯ rails c
Loading development environment (Rails 5.2.4.4)
[1] pry(main)> ActiveStorage::Blob.unattached
ActiveStorage::Blob Load (1.0ms) SELECT "active_storage_blobs".* FROM "active_storage_blobs" LEFT OUTER JOIN "active_storage_attachments" ON "active_storage_attachments"."blob_id" = "active_storage_blobs"."id" WHERE "active_storage_attachments"."blob_id" IS NULL
これを使えば以下のように宙ぶらりんになった active_storage_blobs
のレコードと紐づく実体ファイルを削除することができる。
[1] pry(main)> ActiveStorage::Blob.unattached.each(&:purge)
ActiveStorage::Blob Load (1.0ms) SELECT "active_storage_blobs".* FROM "active_storage_blobs" LEFT OUTER JOIN "active_storage_attachments" ON "active_storage_attachments"."blob_id" = "active_storage_blobs"."id" WHERE "active_storage_attachments"."blob_id" IS NULL
(0.3ms) BEGIN
ActiveStorage::Attachment Exists (0.2ms) SELECT 1 AS one FROM "active_storage_attachments" WHERE "active_storage_attachments"."blob_id" = $1 LIMIT $2 [["blob_id", 1], ["LIMIT", 1]]
ActiveStorage::Blob Destroy (0.5ms) DELETE FROM "active_storage_blobs" WHERE "active_storage_blobs"."id" = $1 [["id", 1]]
(0.8ms) COMMIT
S3 Storage (105.5ms) Deleted file from key: ...
=> nil
ActiveStorage::Blob
クラスに unattached
というスコープとして以下のように定義されている。(Rails v5.2時点)
activestorage/app/models/active_storage/blob.rb
class ActiveStorage::Blob < ActiveRecord::Base
...
has_many :attachments
scope :unattached, -> { left_joins(:attachments).where(ActiveStorage::Attachment.table_name => { blob_id: nil }) }
...
end
メモ:現時点でのmasterブランチには missing メソッドを使うように変更するコミットが入っているよう。
activestorage/app/models/active_storage/blob.rb
@@ -46,7 +46,7 @@ class ActiveStorage::Blob < ActiveRecord::Base
has_many :attachments
- scope :unattached, -> { left_joins(:attachments).where(ActiveStorage::Attachment.table_name => { blob_id: nil }) }
+ scope :unattached, -> { where.missing(:attachments) }