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)

请注意,与不删除后端文件的更新一样,删除实例模型(此处为照片)不会删除后端文件,无论如何在 Django 1.3 中都不会,您必须添加更多自定义代码这样做(或定期做一些肮脏的 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

这是一个可以使用或不使用 or 的代码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 回答
1

有很多关于这个问题的票,尽管这很可能不会成为核心。最全面的是http://code.djangoproject.com/ticket/11663。如果您正在寻找解决方案,补丁和票证评论可以为您提供一些指导。

您还可以考虑使用不同的 StorageBackend,例如 Django 片段 976. http://djangosnippets.org/snippets/976/给出的覆盖文件存储系统。您可以将默认存储更改为此后端,也可以在每个 FileField/ImageField 声明中覆盖它。

于 2011-01-17T19:04:50.697 回答
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 回答
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 回答