当我从数据库/模型中删除一个对象时,如何让Django Admin删除文件?

我正在使用1.2.5标准的ImageField并使用内置存储后端。 file upload的罚款,但是当我从pipe理员删除一个条目服务器上的实际文件不会删除。

您可以接收pre_delete信号并调用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) 

Django的1.5解决scheme:我使用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

尝试Django的清理

 pip install django-cleanup 

settings.py

 INSTALLED_APPS = ( ... 'django_cleanup', # should go after your apps ) 

这个代码也可以在pipe理面板上运行在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) 

在删除模型之前获取存储和path是非常重要的,否则在删除模型时,后者将保持无效。

需要 deleteupdate实际的文件。

 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自动删除被删除的原因同样适用于此。 如果你删除一个在其他地方被引用的文件,你将会遇到问题。

在我的情况下,这似乎是适当的,因为我有一个专用的文件模型来pipe理我的所有文件。

注意:出于某种原因,post_delete似乎不能正常工作。 该文件被删除,但数据库logging保持,这是完全相反,我所期望的,即使在错误的条件下。 pre_delete虽然工作正常。

也许有点晚了。 但最简单的方法是使用post_save信号。 只要记住,即使在QuerySet删除过程中,信号也是可以执行的,但是在QuerySet删除过程中不会执行[model] .delete()方法,所以它不是最好的select。

核心/ 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) """--------------------------------------------------------------------------""" 

核心/ signals.py

 import os def delete_image_slide(sender, **kwargs): slide = kwargs.get('instance') try: os.remove(slide.image.path) except: pass 

这个function将在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) 

使用post_delete肯定是正确的路要走。 有时候,事情可能会出错,文件不会被删除。 当然,在使用post_delete之前,你有一堆没有被删除的旧文件。 我创build了一个函数,删除对象的文件,如果对象引用的文件不存在然后删除对象,如果该文件没有对象,那么也删除,也可以删除基于一个“活跃”标志为对象..我添加到我的大多数模型的东西。 您必须将要检查的对象,对象文件的path,文件字段和用于删除非活动对象的标志传递给它:

 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 

确保你在文件前写上“ self ”。 所以上面的例子应该是

 def delete(self, *args, **kwargs): self.somefile.delete() super(MyModel, self).delete(*args, **kwargs) 

我已经忘记了我的文件之前的“自我”,并没有像在全局命名空间中看到的那样工作。

如果您的项目中已经有一些未使用的文件,并且想删除它们,可以使用django实用程序django-unused-media

我可能有一个特殊的情况,因为我使用的dynamic目录名称的文件字段上的upload_to选项,但我发现的解决scheme是使用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)