我正在使用1.2.5版本,使用标准的ImageField并使用内置的存储后端。文件上传正常,但当我从管理员中删除一个条目时,实际上服务器上的文件并没有被删除。
我正在使用1.2.5版本,使用标准的ImageField并使用内置的存储后端。文件上传正常,但当我从管理员中删除一个条目时,实际上服务器上的文件并没有被删除。
pre_delete
或post_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)
MEDIA_ROOT
目录之前,检查instance.file
字段是否为空。即使是ImageField(null=False)
字段也要这样做。如果该字段不为空,则不要删除整个目录,以免发生意外情况。 - Antony Hatchkinspost_delete
信号,因为在删除失败的情况下,这样更安全。这样既不会删除模型也不会删除文件,保持数据的一致性。如果我对 post_delete
和 pre_delete
信号的理解有误,请纠正我。 - toto_ticopip install django-cleanup
设置.py
INSTALLED_APPS = (
...
'django_cleanup.apps.CleanupConfig',
)
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
字段是我Photo
模型中的ImageField
。
NotImplementedError: This backend doesn't support absolute paths.
的错误。您可以轻松地通过将文件字段的名称传递给storage.delete()
来解决此问题,而不是文件字段的路径。例如,将此答案的最后两行替换为 storage, name = photo.original_image.storage, photo.original_image.name
然后 storage.delete(name)
。 - Sean Azlinpost_delete
中使用mymodel.myimagefield.delete(save=False)
吗?换句话说,我知道可以删除文件,但是当包含imagefield的模型被删除时,你能否删除文件呢? - eugenepost_delete
中,你需要使用 instance
,并执行 instance.myimagefield.delete(save=False)
。 - user2616836这段代码在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)
在删除模型之前获取存储和路径非常重要,否则即使已被删除,后者也将保留。
delete
不总是在行被删除时被调用,你必须使用信号。 - lvella在删除
和更新
时,您需要删除实际文件。
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)
https://docs.djangoproject.com/en/dev/topics/signals/
当然,删除FileField自动删除功能被删除的原因同样适用于这里。如果您删除了在其他地方被引用的文件,就会出现问题。post_delete
不起作用,因为 file_field.delete()
默认会将模型保存到数据库中,请尝试使用 file_field.delete(False)
。更多信息请参阅 https://docs.djangoproject.com/en/1.3/ref/models/fields/#django.db.models.FieldFile.delete。 - Adam Jurczykfrom 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)
"""--------------------------------------------------------------------------"""
core/signals.py
import os
def delete_image_slide(sender, **kwargs):
slide = kwargs.get('instance')
try:
os.remove(slide.image.path)
except:
pass
这个功能将在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)
MyModel.objects.all()[0].delete()
将删除文件,而MyModel.objects.all().delete()
则不会。请使用信号。 - Antony Hatchkinsdef 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