Django FileField を削除する 質問する

Django FileField を削除する 質問する

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()

データベースからレコードは削除されますが、サーバー上のファイルは削除されません。他に試せる方法はありますか?

ありがとう!

ベストアンサー1

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()FileFieldFieldFile

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

  • Django がファイルを自動的に削除しない理由:Django 1.3 のリリースノートのエントリ

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

  • pre_delete信号のみを使用する例

おすすめ記事