26

Django では、モデルに ImageFile がある場合、削除すると関連するファイルがディスクから削除され、データベースからレコードが削除されます。

イメージを置き換えると、不要なファイルがディスクから削除されませんか? 代わりに、オリジナルを保持し、代替を追加していることがわかります。

オブジェクトを削除しても、元のファイルは削除されず、置換のみが削除されるようになりました。

これを行うための良い戦略はありますか?ユーザーが画像を頻繁に交換する場合、孤立したファイルがたくさんあるのは望ましくありません。

4

8 に答える 8

32

私が見つけた最善の戦略は、モデルにカスタム保存メソッドを作成することです:

class Photo(models.Model):

    image = ImageField(...) # works with FileField also

    def save(self, *args, **kwargs):
        # delete old file when replacing by updating the file
        try:
            this = Photo.objects.get(id=self.id)
            if this.image != self.image:
                this.image.delete(save=False)
        except: pass # when new photo then we do nothing, normal case          
        super(Photo, self).save(*args, **kwargs)

バックエンド ファイルを削除しない更新と同様に、インスタンス モデル (ここでは写真) を削除してもバックエンド ファイルは削除されないことに注意してください。それをしてください(または定期的にいくつかの汚いcronジョブを実行してください)。

最後に、ForeignKey、ManytoMany、およびその他の関係を使用してすべての更新/削除ケースをテストし、バックエンド ファイルが正しく削除されているかどうかを確認します。あなたがテストしたものだけを信じてください

于 2011-12-01T13:37:57.253 に答える
15

イメージを置き換えると、不要なファイルもディスクから削除されるべきではありませんか?

昔は、FileField孤立したファイルをクリーンアップすることに熱心でした。しかし、それはDjango 1.2で変更されました:

以前のDjangoバージョンでは、FileFieldを含むモデルインスタンスが削除されると、FileFieldはそれ自体を使用して、バックエンドストレージからファイルも削除していました。これにより、ロールバックされたトランザクションや同じファイルを参照する異なるモデルのフィールドなど、いくつかの潜在的に深刻なデータ損失シナリオへの扉が開かれました。Django 1.2.5では、FileFieldがバックエンドストレージからファイルを削除することはありません。

于 2013-02-04T04:08:47.537 に答える
9

次の作業例のコードは、ImageField に画像をアップロードするときに、同じ名前のファイルが存在するかどうかを検出し、その場合、新しいファイルを保存する前にそのファイルを削除します。

ファイル名に関係なく古いファイルを削除するように簡単に変更できます。しかし、それは私のプロジェクトで望んでいたことではありません。

次のクラスを追加します。

from django.core.files.storage import FileSystemStorage
class OverwriteStorage(FileSystemStorage):
    def _save(self, name, content):
        if self.exists(name):
            self.delete(name)
        return super(OverwriteStorage, self)._save(name, content)

    def get_available_name(self, name):
        return name

そして、次のように ImageField で使用します。

class MyModel(models.Model):
    myfield = models.ImageField(
        'description of purpose',
        upload_to='folder_name',
        storage=OverwriteStorage(),  ### using OverwriteStorage here
        max_length=500,
        null=True,
        blank=True,
        height_field='height',
        width_field='width'
    )
    height = models.IntegerField(blank=True, null=True)
    width = models.IntegerField(blank=True, null=True)
于 2012-07-04T09:14:11.670 に答える
2

トランザクションを使用しない場合、またはトランザクションのロールバックでファイルが失われることを恐れない場合は、django-cleanupを使用できます。

于 2012-09-12T05:47:15.177 に答える
1

この問題に関する多くのチケットがありましたが、これがコアにならない可能性があります。最も包括的なものはhttp://code.djangoproject.com/ticket/11663です。パッチとチケットのコメントは、解決策を探している場合に何らかの方向性を示します。

Django snippet 976 ( http://djangosnippets.org/snippets/976/ ) で提供されている Overwrite File Storage System など、別の StorageBackend の使用を検討することもできます。デフォルトのストレージをこのバックエンドに変更するか、各 FileField/ImageField 宣言で上書きすることができます。

于 2011-01-17T19:04:50.697 に答える
1

upload_to=...以下は、またはblank=Trueの有無に関係なく、送信されたファイルが古いファイルと同じ名前の場合に機能するコードです。

(py3 構文、Django 1.7 でテスト済み)

class Attachment(models.Model):

    document = models.FileField(...)  # or ImageField

    def delete(self, *args, **kwargs):
        self.document.delete(save=False)
        super().delete(*args, **kwargs)

    def save(self, *args, **kwargs):
        if self.pk:
            old = self.__class__._default_manager.get(pk=self.pk)
            if old.document.name and (not self.document._committed or not self.document.name):
                old.document.delete(save=False)
        super().save(*args, **kwargs)

この種の解決策は、非トランザクション コンテキストにいる場合にのみ適用できることに注意してください (ファイルが完全に失われるため、ロールバックはありません)。

于 2014-11-28T11:03:42.853 に答える
0

元のファイルを保存し、変更されている場合は削除します。

class Document(models.Model):
    document = FileField()

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self._document = self.document

    def save(self, *args, **kwargs):
        if self.document != self._document:
            self._document.delete()
            super().save(*args, **kwargs)
于 2018-11-01T11:02:02.813 に答える
0

で簡単な方法を使用したpopenため、モデルを保存するときInfoに、新しいファイルにリンクする前に以前のファイルを削除します。

import os

try:
    os.popen("rm %s" % str(info.photo.path))
except:
    #deal with error
    pass
info.photo = nd['photo']
于 2011-01-17T15:18:15.703 に答える