91

我正在使用带有标准 ImageField 的 1.2.5 并使用内置存储后端。文件上传正常,但是当我从管理员中删除条目时,服务器上的实际文件不会删除。

4

13 回答 13

108

您可以接收pre_deleteorpost_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)
于 2013-01-14T01:11:47.770 回答
53

尝试django-cleanup

pip install django-cleanup

设置.py

INSTALLED_APPS = (
    ...
    'django_cleanup.apps.CleanupConfig',
)
于 2015-03-17T17:21:48.867 回答
35

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模型中。

于 2013-05-08T14:15:20.037 回答
18

这段代码在 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)

在删除模型之前获取存储和路径很重要,否则如果删除,后者也将保持无效。

于 2012-07-04T17:28:52.397 回答
16

需要删除 和 上的实际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)
于 2015-09-18T01:30:06.377 回答
6

您可以考虑使用 pre_delete 或 post_delete 信号:

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

当然,取消 FileField 自动删除的原因同样适用于此。如果您删除在其他地方引用的文件,您将遇到问题。

就我而言,这似乎很合适,因为我有一个专用的文件模型来管理我的所有文件。

注意:由于某种原因 post_delete 似乎无法正常工作。该文件被删除,但数据库记录仍然存在,这与我的预期完全相反,即使在错误情况下也是如此。pre_delete 工作正常。

于 2011-11-08T16:50:40.147 回答
3

也许有点晚了。但对我来说最简单的方法是使用 post_save 信号。请记住,即使在 QuerySet 删除过程中也会执行信号,但是在 QuerySet 删除过程中不会执行 [model].delete() 方法,因此它不是覆盖它的最佳选择。

核心/模型.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)
"""--------------------------------------------------------------------------"""

核心/信号.py

import os

def delete_image_slide(sender, **kwargs):
    slide = kwargs.get('instance')
    try:
        os.remove(slide.image.path)
    except:
        pass
于 2013-08-16T02:26:07.060 回答
1

使用 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
于 2014-06-05T23:22:10.773 回答
1

这个功能将在 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)
于 2011-03-21T02:54:51.507 回答
0

确保在文件前写上“ self ”。所以上面的例子应该是

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

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

我忘记了我的文件之前的“自我”,因为它在全局命名空间中查找时不起作用。

于 2013-03-27T17:15:01.897 回答
0

Django 2.x 解决方案:

无需安装任何软件包!在Django 2中很容易处理。我尝试过使用 Django 2 和 SFTP 存储的以下解决方案(但我认为它适用于任何存储)

首先编写一个自定义管理器。因此,如果您希望能够使用objects方法删除模型的文件,则必须编写并使用 [Custom Manager][3](用于覆盖delete()方法objects):

class CustomManager(models.Manager):
    def delete(self):
        for obj in self.get_queryset():
            obj.delete()

现在您必须先删除image,然后再删除模型本身并将 分配CustomManager给模型,您必须在模型中初始化objects

class MyModel(models.Model):
    image = models.ImageField(upload_to='/pictures/', blank=True)
    objects = CustomManager() # add CustomManager to model
    def delete(self, using=None, keep_parents=False):

    objects = CustomManager() # just add this line of code inside of your model

    def delete(self, using=None, keep_parents=False):
        self.image.storage.delete(self.song.name)
        super().delete()
于 2019-10-25T11:14:07.513 回答
0

如果您的项目中已经有许多未使用的文件并且想要删除它们,您可以使用 django 实用程序django-unused-media

于 2017-01-11T18:50:44.700 回答
-1

我可能有一个特殊情况,因为我在我的文件字段上使用带有动态目录名称的 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)
于 2013-02-17T23:01:55.420 回答