1. ホーム
  2. django

[解決済み] Django の FileField の削除

2022-08-19 18:11:18

質問

DjangoでWebアプリを作っています。私はファイルをアップロードするモデルを持っていますが、私はファイルを削除することはできません。以下は私のコードです。

class Song(models.Model):
    name = models.CharField(blank=True, max_length=100)
    author = models.ForeignKey(User, to_field='id', related_name="id_user2")
    song = models.FileField(upload_to='/songs/')
    image = models.ImageField(upload_to='/pictures/', blank=True)
    date_upload = models.DateField(auto_now_add=True)

    def delete(self, *args, **kwargs):
        # You have to prepare what you need before delete the model
        storage, path = self.song.storage, self.song.path
        # Delete the model before the file
        super(Song, self).delete(*args, **kwargs)
        # Delete the file after the model
        storage.delete(path)

次に python manage.py shell こうしています。

song = Song.objects.get(pk=1)
song.delete()

それはデータベースからレコードを削除しますが、サーバー上のファイルは削除されません。 他に何を試すことができますか?

ありがとうございます。

どのように解決するのですか?

Django 1.3 以前では、対応するモデルインスタンスを削除すると、ファイルは自動的にファイルシステムから削除されました。おそらく新しい Django のバージョンを使っているので、自分でファイルシステムからファイルを削除するように実装する必要があります。

シンプルなシグナルベースのサンプル

この記事を書いている時点では、私が選んだ方法は、ミックスされた post_deletepre_save シグナルを使用することで、対応するモデルが削除されたり、ファイルが変更されたりするたびに、古いファイルが削除されるようになります。

仮想的な MediaFile というモデルを使っています。

import os
import uuid

from django.db import models
from django.dispatch import receiver
from django.utils.translation import ugettext_lazy as _


class MediaFile(models.Model):
    file = models.FileField(_("file"),
        upload_to=lambda instance, filename: str(uuid.uuid4()))


# These two auto-delete files from filesystem when they are unneeded:

@receiver(models.signals.post_delete, sender=MediaFile)
def auto_delete_file_on_delete(sender, instance, **kwargs):
    """
    Deletes file from filesystem
    when corresponding `MediaFile` object is deleted.
    """
    if instance.file:
        if os.path.isfile(instance.file.path):
            os.remove(instance.file.path)

@receiver(models.signals.pre_save, sender=MediaFile)
def auto_delete_file_on_change(sender, instance, **kwargs):
    """
    Deletes old file from filesystem
    when corresponding `MediaFile` object is updated
    with new file.
    """
    if not instance.pk:
        return False

    try:
        old_file = MediaFile.objects.get(pk=instance.pk).file
    except MediaFile.DoesNotExist:
        return False

    new_file = instance.file
    if not old_file == new_file:
        if os.path.isfile(old_file.path):
            os.remove(old_file.path)

  • しばらく前に作ったアプリの1つがこのコードを本番で使ったと思いますが、それでも自己責任で使ってください。
  • 例えば データ損失の可能性 の場合、存在しないファイルを参照してしまう可能性があります。 save() メソッド呼び出しがたまたまロールバックされるトランザクション内にあった場合、データは存在しないファイルを参照することになるかもしれません。ファイルを削除するロジックを transaction.on_commit() にラップすることもできます。 transaction.on_commit(lambda: os.remove(old_file.path)) , ミカエルのコメントで提案されているように . django-cleanup ライブラリ はそのような行を行います .
  • エッジケース: アプリが新しいファイルをアップロードし、モデルインスタンスを新しいファイルに指し示すときに、そのファイルに対して save() を呼び出さずに (例えば QuerySet を一括更新するなど)、シグナルが実行されないため、古いファイルが放置されたままになります。これは、従来のファイル処理方法を使用する場合は発生しません。
  • コーディングスタイル: この例では file をフィールド名として使っていますが、これは組み込みの file オブジェクト識別子と衝突するため、良いスタイルではありません。

補遺: 定期的なクリーンアップ

現実的には、次のようなことが考えられます。 を定期的に実行し、実行時の失敗でファイルが削除されなかった場合に、孤児ファイルのクリーンアップを処理したいと思うかもしれません。このことを念頭に置いて、おそらくシグナルハンドラを完全に取り除き、そのようなタスクは タスクは、機密性の低いデータやそれほど大きくないファイルを処理するためのメカニズムです。

しかし、いずれにせよ、もしあなたが を扱っている場合は、関連する法的責任を回避するために、運用中のデータを適時に削除することを怠らないよう、常に二重、三重にチェックしたほうがよいでしょう。

こちらもご覧ください。

  • FieldFile.delete() は Django 1.11 のモデルフィールドリファレンスにあります (このリファレンスでは FieldFile クラスを記述していますが .delete() を直接フィールドで呼び出します。 FileField インスタンスは対応する FieldFile インスタンスへのプロキシであり、そのメソッドにあたかもフィールドのもののようにアクセスします)

    モデルが削除されたとき、関連するファイルは削除されないことに注意してください。孤児となったファイルをクリーンアップする必要がある場合は、自分で処理する必要があります (たとえば、手動で実行するか、cron などで定期的に実行するようにスケジュールできるカスタム管理コマンドを使用します)。

  • なぜ Django はファイルを自動的に削除しないのか。 Django 1.3 のリリースノートにあるエントリ

    以前のバージョンの Django では、モデルインスタンスに FileField が削除されました。 FileField が削除されると、バックエンド ストレージからそのファイルも削除されるように勝手に設定されました。これによって、ロールバックされたトランザクションや、同じファイルを参照する異なるモデルのフィールドなど、いくつかのデータ損失のシナリオへの扉が開かれました。Django 1.3 では、モデルが削除されたときに FileField 's delete() メソッドは呼び出されません。孤児となったファイルのクリーンアップが必要な場合は、自分で処理する必要があります (たとえば、手動で実行したり、cron などで定期的に実行するようスケジュールできるカスタム管理コマンドを使用します)。

  • の使用例 pre_delete 信号のみ