替换Django图片时未删除原始文件

26 投票
8 回答
13971 浏览
提问于 2025-04-16 08:16

在Django中,如果你的模型里有一个ImageFile,删除这个记录时,会同时把磁盘上的文件也删掉,数据库里的记录也会被删除。

那么,替换一张图片时,难道不应该把不需要的文件也从磁盘上删除吗?可是我发现,它会保留原来的文件,并且把新的文件加上去。

这样一来,删除这个对象时,只会删除新替换的文件,而原来的文件却不会被删掉。

有没有什么好的方法来解决这个问题呢?我不想让我的用户频繁替换图片后,留下很多无用的文件。

8 个回答

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

然后像这样使用它和图片字段:

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

替换图片的时候,难道不应该把不需要的文件也从磁盘上删除吗?

在以前的版本中,FileField 会主动清理那些孤立的文件。但是在Django 1.2之后,这种情况发生了变化:

在早期的Django版本中,当一个包含FileField的模型实例被删除时,FileField会自动把后端存储中的文件也删除。这就可能导致一些严重的数据丢失问题,比如事务回滚和不同模型的字段引用同一个文件。在Django 1.2.5中,FileField将不会再从后端存储中删除文件。

34

我发现的最好方法是在模型里创建一个自定义的保存方法:

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)

需要注意的是,就像更新操作不会删除后端文件一样,删除一个实例模型(这里是照片)也不会删除后端文件,尤其是在Django 1.3版本中。你需要添加更多的自定义代码来实现这一点(或者定期做一些麻烦的定时任务)。

最后,测试你所有的更新和删除操作,特别是涉及到外键、多个关系等,确保后端文件被正确删除。只相信你测试过的结果

撰写回答