¿Cómo obtengo que Django Admin elimine archivos cuando elimino un objeto de la base de datos / modelo?

Estoy usando 1.2.5 con un ImageField estándar y usando el back-end de almacenamiento incorporado. Los archivos se cargan bien, pero cuando elimino una entrada del administrador, el archivo real en el servidor no se elimina.

Puede recibir la señal pre_delete o post_delete (vea el comentario de @ toto_tico a continuación) y llame al método de eliminación en el objeto FileField, por lo tanto (en 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) 

Solución Django 1.5: utilizo post_delete por varias razones que son internas de mi aplicación.

 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) 

Puse esto en la parte inferior del archivo models.py.

el campo original_image es el ImageField en mi modelo de Photo .

Prueba django-cleanup

 pip install django-cleanup 

settings.py

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

Este código funciona bien en Django 1.4 también con el panel de administración.

 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) 

Es importante obtener el almacenamiento y la ruta antes de eliminar el modelo o este último persistirá también si se elimina.

Debe eliminar el archivo real tanto en delete como en 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) 

Puede considerar usar una señal pre_delete o post_delete:

https://docs.djangoproject.com/en/dev/topics/signals/

Por supuesto, las mismas razones por las que se eliminó la eliminación automática de FileField también se aplican aquí. Si elimina un archivo al que se hace referencia en otro lugar, tendrá problemas.

En mi caso esto pareció apropiado porque tenía un modelo de archivo dedicado para administrar todos mis archivos.

Nota: Por algún motivo, post_delete no parece funcionar bien. El archivo se eliminó, pero el registro de la base de datos se mantuvo, lo que es completamente contrario a lo que yo esperaba, incluso en condiciones de error. pre_delete funciona bien sin embargo.

Tal vez es un poco tarde. Pero la forma más fácil para mí es usar una señal post_save. Solo recordar que las señales se ejecutan incluso durante un proceso de eliminación de QuerySet, pero el método [model] .delete () no se ejecuta durante el proceso de eliminación de QuerySet, por lo que no es la mejor opción para anularlo.

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 

Esta funcionalidad se eliminará en Django 1.3, así que no confiaría en ella.

Puede anular el método de delete del modelo en cuestión para eliminar el archivo antes de eliminar por completo la entrada de la base de datos.

Editar:

Aquí hay un ejemplo rápido.

 class MyModel(models.Model): self.somefile = models.FileField(...) def delete(self, *args, **kwargs): somefile.delete() super(MyModel, self).delete(*args, **kwargs) 

Usar el post_delete es seguro el camino correcto a seguir. A veces, aunque las cosas pueden salir mal, los archivos no se eliminan. Por supuesto, hay muchos archivos viejos que no se eliminaron antes de usar post_delete. Creé una función que elimina archivos para objetos en función de si el archivo al que hace referencia el objeto no existe, luego elimina el objeto, si el archivo no tiene un objeto, luego también lo elimina, también puede eliminarse en base a un indicador “activo” para un objeto … Algo que agregué a la mayoría de mis modelos. Debe pasar los objetos que desea verificar, la ruta a los archivos de objetos, el campo de archivo y un indicador para eliminar objetos inactivos:

 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." 

Así es como puedes usar esto:

 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 

asegúrese de escribir ” self ” antes del archivo. por lo que el ejemplo anterior debe ser

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

Me olvidé del “yo” antes de mi archivo y eso no funcionó ya que estaba buscando en el espacio de nombres global.

Si ya tiene una cantidad de archivos sin usar en su proyecto y desea eliminarlos, puede usar la utilidad django django-unused-media

Es posible que tenga un caso especial ya que estoy usando la opción upload_to en mi campo de archivo con nombres de directorio dynamics, pero la solución que encontré fue usar os.rmdir.

En modelos:

 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)