在 Django 中,如果模型中有一个 ImageFile,则删除将从磁盘中删除关联的文件以及从数据库中删除记录。
替换图像不应该同时从磁盘中删除不需要的文件吗?相反,我看到它保留了原件并添加了替换件。
现在删除对象不会删除原始文件,只会删除替换。
有没有什么好的策略来做到这一点?如果我的用户经常替换他们的图像,我不希望有一堆孤立文件。
在 Django 中,如果模型中有一个 ImageFile,则删除将从磁盘中删除关联的文件以及从数据库中删除记录。
替换图像不应该同时从磁盘中删除不需要的文件吗?相反,我看到它保留了原件并添加了替换件。
现在删除对象不会删除原始文件,只会删除替换。
有没有什么好的策略来做到这一点?如果我的用户经常替换他们的图像,我不希望有一堆孤立文件。
我发现的最佳策略是在模型中创建自定义保存方法:
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 和其他关系测试你所有的更新/删除案例,以检查后端文件是否被正确删除。只相信你所测试的。
替换图像不应该同时从磁盘中删除不需要的文件吗?
在过去,FileField
渴望清理孤立的文件。但这在Django 1.2中发生了变化:
在早期的 Django 版本中,当删除包含 FileField 的模型实例时,FileField 会自行从后端存储中删除文件。这为几种潜在的严重数据丢失情况打开了大门,包括回滚事务和引用同一文件的不同模型上的字段。在 Django 1.2.5 中,FileField 永远不会从后端存储中删除文件。
以下工作示例中的代码将在 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)
如果不使用事务或者不怕事务回滚时丢失文件,可以使用django-cleanup
这是一个可以使用或不使用 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)
请记住,这种解决方案仅在您处于非事务性上下文中时才适用(没有回滚,因为文件肯定会丢失)
有很多关于这个问题的票,尽管这很可能不会成为核心。最全面的是http://code.djangoproject.com/ticket/11663。如果您正在寻找解决方案,补丁和票证评论可以为您提供一些指导。
您还可以考虑使用不同的 StorageBackend,例如 Django 片段 976. http://djangosnippets.org/snippets/976/给出的覆盖文件存储系统。您可以将默认存储更改为此后端,也可以在每个 FileField/ImageField 声明中覆盖它。
我使用了一个简单的方法popen
,所以当我保存我的Info
模型时,我在链接到新文件之前删除了以前的文件:
import os
try:
os.popen("rm %s" % str(info.photo.path))
except:
#deal with error
pass
info.photo = nd['photo']
我保存原始文件,如果它已更改 - 删除它。
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)