我将1.2.5与标准ImageField一起使用,并使用内置的存储后端。文件上传正常,但是当我从管理员删除条目时,服务器上的实际文件不会删除。
Answers:
您可以接收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)
instance.file
字段是否为空,或者可以(至少尝试删除)整个MEDIA_ROOT目录。这甚至适用于ImageField(null=False)
字段。
post_delete
信号,因为在由于某种原因导致删除失败的情况下,它更安全。然后,既不保留模型,也不删除文件,以保持数据一致。如果我的理解post_delete
和pre_delete
信号不正确,请纠正我。
pip install django-cleanup
settings.py
INSTALLED_APPS = (
...
'django_cleanup', # should go after your apps
)
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
字段ImageField
在我的Photo
模型中。
NotImplementedError: This backend doesn't support absolute paths.
可以轻松地解决此问题,只需将文件字段的名称传递给storage.delete()
而不是文件字段的路径即可。例如,用storage, name = photo.original_image.storage, photo.original_image.name
then代替该答案的最后两行storage.delete(name)
。
mymodel.myimagefield.delete(save=False)
。docs.djangoproject.com/en/dev/ref/files/file/...
mymodel.myimagefield.delete(save=False)
上post_delete
?换句话说,我可以删除文件,但是当具有图像字段的模型被删除时,可以删除文件吗?
post_delete
执行操作时instance.myimagefield.delete(save=False)
,请注意的使用instance
。
此代码还可以通过“管理”面板在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
在删除行时并不总是被调用,您必须使用信号。
您需要删除同时在实际的文件delete
和update
。
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)
您可以考虑使用pre_delete或post_delete信号:
https://docs.djangoproject.com/en/dev/topics/signals/
当然,删除FileField自动删除的相同原因也适用于此。如果删除在其他地方引用的文件,则会出现问题。
就我而言,这似乎是适当的,因为我有一个专用的文件模型来管理所有文件。
注意:由于某种原因,post_delete似乎无法正常工作。文件被删除,但是数据库记录仍然保留,这与我所期望的完全相反,即使在错误情况下也是如此。pre_delete工作正常。
post_delete
不会工作,因为file_field.delete()
在默认情况下保存模型数据库,尝试file_field.delete(False)
docs.djangoproject.com/en/1.3/ref/models/fields/...
也许有点晚了。但是对我来说,最简单的方法是使用post_save信号。只是要记住,即使在QuerySet删除过程中也会执行信号,但是在QuerySet删除过程中不会执行[model] .delete()方法,因此这不是重写它的最佳选择。
core / models.py:
from 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()
不会删除。使用信号。
确保使用post_delete是正确的方法。有时候,虽然事情可能会出错,但文件不会被删除。当然,在使用post_delete之前,您可能有一堆旧文件没有被删除。我创建了一个函数,该函数根据对象引用的文件不存在然后删除对象来删除对象的文件,如果文件中没有对象,则删除对象,也可以基于“活动”标志删除对象。对象..我添加到大多数模型中的东西。您必须向其传递要检查的对象,对象文件的路径,文件字段以及删除不活动对象的标志:
def 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
如果您的项目中已经有许多未使用的文件并想要删除它们,则可以使用django实用程序django-unused-media
无需安装任何软件包!在Django 2中非常容易处理。我已经尝试过使用Django 2和SFTP存储的以下解决方案(但是我认为它可以与任何存储一起使用)
首先编写一个自定义管理器。因此,如果您希望能够使用objects
方法删除模型文件,则必须编写并使用[Custom Manager] [3](用于的覆盖delete()
方法objects
):
class CustomManager(models.Manager):
def delete(self):
for obj in self.get_queryset():
obj.delete()
现在,您必须先删除,image
然后再删除模型本身,然后再将模型分配CustomManager
给模型,您必须objects
在模型内部进行初始化:
class MyModel(models.Model):
image = models.ImageField(upload_to='/pictures/', blank=True)
objects = CustomManager() # add CustomManager to model
def delete(self, using=None, keep_parents=False):
objects = CustomManager() # just add this line of code inside of your model
def delete(self, using=None, keep_parents=False):
self.image.storage.delete(self.song.name)
super().delete()
我可能有一个特殊情况,因为我在具有动态目录名称的文件字段上使用了upload_to选项,但是我发现的解决方案是使用os.rmdir。
在型号中:
import os
...
class Some_Model(models.Model):
save_path = models.CharField(max_length=50)
...
def delete(self, *args,**kwargs):
os.rmdir(os.path.join(settings.MEDIA_ROOT, self.save_path)
super(Some_Model,self).delete(*args, **kwargs)