Как получить Django Admin для удаления файлов при удалении объекта из базы данных/модели?

Я использую 1.2.5 со стандартным ImageField и использую встроенный сервер хранения. Файлы загружаются нормально, но когда я удаляю запись из admin, фактический файл на сервере не удаляется.

Ответ 1

Вы можете получить сигнал pre_delete или post_delete (см. комментарий @toto_tico ниже) и вызвать метод удаления на объекте FileField, таким образом (в 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)

Ответ 2

Решение 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.

Ответ 3

Попробуйте django-cleanup

pip install django-cleanup

settings.py

INSTALLED_APPS = (
    ...
    'django_cleanup', # should go after your apps
)

Ответ 4

Этот код хорошо работает на Django 1.4 также с панелью Admin.

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)

Важно, чтобы получить хранилище и путь до удаления модели, или последняя будет сохраняться и в случае, если она удалена.

Ответ 5

Вам нужно удалить фактический файл на delete и update.

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)

Ответ 6

Вы можете использовать сигнал pre_delete или post_delete:

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

Конечно, те же причины, по которым было удалено автоматическое удаление FileField, также применяются здесь. Если вы удалите файл, на который ссылаетесь где-то еще, у вас будут проблемы.

В моем случае это казалось подходящим, потому что у меня была специальная файловая модель для управления всеми моими файлами.

Примечание. По какой-то причине post_delete не работает правильно. Файл был удален, но запись базы данных осталась, что полностью противоположно тому, что я ожидал бы, даже в условиях ошибок. pre_delete работает нормально, хотя.

Ответ 7

Может быть, немного поздно. Но самый простой способ для меня - использовать сигнал post_save. Просто помните, что сигналы исключаются даже во время процесса удаления QuerySet, но метод [model].delete() не исключается во время процесса удаления QuerySet, поэтому он не лучший вариант для его отмены.

ядро ​​/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)
"""--------------------------------------------------------------------------"""

ядро ​​/signals.py

import os

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

Ответ 8

Эта функциональность будет удалена в Django 1.3, поэтому я бы не стал полагаться на нее.

Вы можете переопределить метод delete рассматриваемой модели, чтобы удалить файл, прежде чем полностью удалить запись из базы данных.

Edit:

Вот краткий пример.

class MyModel(models.Model):

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

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

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

Ответ 9

Использование post_delete - это, безусловно, правильный путь. Иногда, хотя все может пойти не так, и файлы не удаляются. Конечно, у вас есть куча старых файлов, которые не были удалены до использования post_delete. Я создал функцию, которая удаляет файлы для объектов на основе, если файл ссылки на объекты не существует, а затем удалить объект, если файл не имеет объекта, а затем удалить, также он может быть удален на основе "активного" флага для object.. Что-то я добавил к большинству моих моделей. Вы должны передать ему объекты, которые хотите проверить, путь к файлам объектов, поле файла и флаг для удаления неактивных объектов:

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

Ответ 10

убедитесь, что перед файлом вы пишете " self". поэтому пример выше должен быть

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

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

Я забыл "я" перед своим файлом, и это не сработало, поскольку оно просматривалось в глобальном пространстве имен.

Ответ 11

Если у вас уже есть количество неиспользуемых файлов в вашем проекте и вы хотите их удалить, вы можете использовать утилиту django django-unused-media

Ответ 12

У меня может быть специальный случай, так как я использую параметр upload_to в своем поле с именами динамических каталогов, но решение, которое я нашел, это использовать os.rmdir.

В моделях:

import os

...

class Some_Model(models.Model):
     save_path = models.CharField(max_length=50)
     ...
     def delete(self, *args,**kwargs):
          os.rmdir(os.path.join(settings.MEDIA_ROOT, self.save_path)
          super(Some_Model,self).delete(*args, **kwargs)