web-dev-qa-db-ja.com

データベース/モデルからオブジェクトを削除するときにファイルを削除するには、Django Adminを取得するにはどうすればよいですか?

標準のImageFieldで1.2.5を使用し、組み込みのストレージバックエンドを使用しています。ファイルは正常にアップロードされますが、管理者からエントリを削除しても、サーバー上の実際のファイルは削除されません。

83
narkeeso

pre_deleteまたはpost_deleteシグナル(以下の@toto_ticoのコメントを参照)およびFileFieldオブジェクトで delete() メソッドを呼び出すため、(models.pyで):

class MyModel(models.Model):
    file = models.FileField()
    ...

# Receive the pre_delete signal and delete the file associated with the model instance.
from Django.db.models.signals import pre_delete
from Django.dispatch.dispatcher import receiver

@receiver(pre_delete, sender=MyModel)
def mymodel_delete(sender, instance, **kwargs):
    # Pass false so FileField doesn't save the model.
    instance.file.delete(False)
96
darrinm

試してください Django-cleanup

pip install Django-cleanup

settings.py

INSTALLED_APPS = (
    ...
    'Django_cleanup', # should go after your apps
)
41
un1t

Django 1.5ソリューション:私はアプリの内部にあるさまざまな理由でpost_deleteを使用しています。

from Django.db.models.signals import post_delete
from Django.dispatch import receiver

@receiver(post_delete, sender=Photo)
def photo_post_delete_handler(sender, **kwargs):
    photo = kwargs['instance']
    storage, path = photo.original_image.storage, photo.original_image.path
    storage.delete(path)

私はこれをmodels.pyファイルの下部に貼り付けました。

original_imageフィールドは、私のImageFieldモデルのPhotoです。

34
Kushal

このコードは、管理パネルでもDjango 1.4で動作します。

class ImageModel(models.Model):
    image = ImageField(...)

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

モデルを削除する前にストレージとパスを取得することが重要です。削除しない場合もモデルは無効のままになります。

17

必要は、deleteupdateの両方の実際のファイルを削除します。

from Django.db import models

class MyImageModel(models.Model):
    image = models.ImageField(upload_to='images')

    def remove_on_image_update(self):
        try:
            # is the object in the database yet?
            obj = MyImageModel.objects.get(id=self.id)
        except MyImageModel.DoesNotExist:
            # object is not in db, nothing to worry about
            return
        # is the save due to an update of the actual image file?
        if obj.image and self.image and obj.image != self.image:
            # delete the old image file from the storage in favor of the new file
            obj.image.delete()

    def delete(self, *args, **kwargs):
        # object is being removed from db, remove the file from storage first
        self.image.delete()
        return super(MyImageModel, self).delete(*args, **kwargs)

    def save(self, *args, **kwargs):
        # object is possibly being updated, if so, clean up.
        self.remove_on_image_update()
        return super(MyImageModel, self).save(*args, **kwargs)
9
un33k

Pre_deleteまたはpost_deleteシグナルの使用を検討できます。

https://docs.djangoproject.com/en/dev/topics/signals/

もちろん、FileFieldの自動削除が削除されたのと同じ理由がここでも当てはまります。他の場所で参照されているファイルを削除すると、問題が発生します。

私の場合、すべてのファイルを管理するための専用のファイルモデルがあるため、これは適切だと思われました。

注:何らかの理由で、post_deleteが正しく機能していないようです。ファイルは削除されましたが、データベースレコードは残りました。これは、エラー状態であっても、予想とはまったく逆です。ただし、pre_deleteは正常に機能します。

6
SystemParadox

多分それは少し遅れています。しかし、私にとって最も簡単な方法は、post_saveシグナルを使用することです。シグナルはQuerySetの削除プロセス中にも実行されますが、[model] .delete()メソッドはQuerySetの削除プロセス中に実行されないため、オーバーライドするのは最適なオプションではありません。

core/models.py:

from Django.db import models
from Django.db.models.signals import post_delete
from core.signals import delete_image_slide
SLIDE1_IMGS = 'slide1_imgs/'

class Slide1(models.Model):
    title = models.CharField(max_length = 200)
    description = models.CharField(max_length = 200)
    image = models.ImageField(upload_to = SLIDE1_IMGS, null = True, blank = True)
    video_embed = models.TextField(null = True, blank = True)
    enabled = models.BooleanField(default = True)

"""---------------------------- SLIDE 1 -------------------------------------"""
post_delete.connect(delete_image_slide, Slide1)
"""--------------------------------------------------------------------------"""

core/signals.py

import os

def delete_image_slide(sender, **kwargs):
    slide = kwargs.get('instance')
    try:
        os.remove(slide.image.path)
    except:
        pass
3
Mauricio

Post_deleteを使用するのが正しい方法です。時には物事がうまくいかないことがありますが、ファイルは削除されません。もちろん、post_deleteが使用される前に削除されなかった古いファイルがたくさんある場合があります。オブジェクトが参照するファイルが存在しない場合に基づいてオブジェクトのファイルを削除する関数を作成し、オブジェクトが削除され、ファイルにオブジェクトがない場合は削除します。また、「アクティブ」フラグに基づいて削除することもできますオブジェクト..ほとんどのモデルに追加したもの。チェックするオブジェクト、オブジェクトファイルへのパス、ファイルフィールド、および非アクティブなオブジェクトを削除するためのフラグを渡す必要があります。

def cleanup_model_objects(m_objects, model_path, file_field='image', clear_inactive=False):
    # PART 1 ------------------------- INVALID OBJECTS
    #Creates photo_file list based on photo path, takes all files there
    model_path_list = os.listdir(model_path)

    #Gets photo image path for each photo object
    model_files = list()
    invalid_files = list()
    valid_files = list()
    for obj in m_objects:

        exec("f = ntpath.basename(obj." + file_field + ".path)")  # select the appropriate file/image field

        model_files.append(f)  # Checks for valid and invalid objects (using file path)
        if f not in model_path_list:
            invalid_files.append(f)
            obj.delete()
        else:
            valid_files.append(f)

    print "Total objects", len(model_files)
    print "Valid objects:", len(valid_files)
    print "Objects without file deleted:", len(invalid_files)

    # PART 2 ------------------------- INVALID FILES
    print "Files in model file path:", len(model_path_list)

    #Checks for valid and invalid files
    invalid_files = list()
    valid_files = list()
    for f in model_path_list:
        if f not in model_files:
            invalid_files.append(f)
        else:
            valid_files.append(f)
    print "Valid files:", len(valid_files)
    print "Files without model object to delete:", len(invalid_files)

    for f in invalid_files:
        os.unlink(os.path.join(model_path, f))

    # PART 3 ------------------------- INACTIVE PHOTOS
    if clear_inactive:
        #inactive_photos = Photo.objects.filter(active=False)
        inactive_objects = m_objects.filter(active=False)
        print "Inactive Objects to Delete:", inactive_objects.count()
        for obj in inactive_objects:
            obj.delete()
    print "Done cleaning model."

これは、これを使用する方法です。

photos = Photo.objects.all()
photos_path, tail = ntpath.split(photos[0].image.path)  # Gets dir of photos path, this may be different for you
print "Photos -------------->"
cleanup_model_objects(photos, photos_path, file_field='image', clear_inactive=False)  # image file is default
1
radtek

この機能はDjango 1.3で削除されるため、これに依存しません。

データベースからエントリを完全に削除する前に、問題のモデルのdeleteメソッドをオーバーライドしてファイルを削除できます。

編集:

以下に簡単な例を示します。

class MyModel(models.Model):

    self.somefile = models.FileField(...)

    def delete(self, *args, **kwargs):
        somefile.delete()

        super(MyModel, self).delete(*args, **kwargs)
1
Derek Reynolds

ファイルの前に「self」と書いてください。したがって、上記の例は

def delete(self, *args, **kwargs):
        self.somefile.delete()

        super(MyModel, self).delete(*args, **kwargs)

ファイルの前に「自己」を忘れてしまいましたが、それはグローバル名前空間で見ていたので機能しませんでした。

0
Bjorn

プロジェクトに未使用のファイルがすでにあり、それらを削除する場合は、Django utility Django-unused-media

0
Andrey Kolpakov