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