Estoy usando 1.2.5 con un ImageField estándar y usando el backend de almacenamiento incorporado. Los archivos se cargan bien, pero cuando elimino una entrada del administrador, el archivo real en el servidor no se elimina.
django
file
storage
django-models
imagefield
narkeeso
fuente
fuente
Respuestas:
Puede recibir la señal
pre_delete
opost_delete
(ver el comentario de @ toto_tico a continuación) y llamar al método delete () en el objeto FileField, así (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)
fuente
instance.file
marca de verificación si el campo no está vacío o puede (al menos intentarlo) eliminar todo el directorio MEDIA_ROOT. Esto se aplica incluso a losImageField(null=False)
campos.post_delete
señal porque es más seguro en caso de que la eliminación falle por cualquier motivo. Entonces ni el modelo, ni el archivo se eliminarían manteniendo los datos consistentes. Corríjame si mi comprensión de las señalespost_delete
y laspre_delete
señales es incorrecta.Prueba django-cleanup
pip install django-cleanup
settings.py
INSTALLED_APPS = ( ... 'django_cleanup', # should go after your apps )
fuente
Solución Django 1.5: uso post_delete por varias razones 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)
Pegué esto en la parte inferior del archivo models.py.
el
original_image
campo es elImageField
de miPhoto
modelo.fuente
NotImplementedError: This backend doesn't support absolute paths.
Puede solucionar esto fácilmente pasando el nombre del campo del archivo a enstorage.delete()
lugar de la ruta del campo del archivo. Por ejemplo, reemplace las dos últimas líneas de esta respuesta constorage, name = photo.original_image.storage, photo.original_image.name
entoncesstorage.delete(name)
.mymodel.myimagefield.delete(save=False)
en su lugar. docs.djangoproject.com/en/dev/ref/files/file/…mymodel.myimagefield.delete(save=False)
enpost_delete
? En otras palabras, puedo ver que puedo eliminar el archivo, pero ¿puedes eliminar el archivo cuando se elimina un modelo que tiene el campo de imagen?post_delete
lo haceinstance.myimagefield.delete(save=False)
, tenga en cuenta el uso deinstance
.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 permanecerá vacío también si se elimina.
fuente
delete
no siempre se llama cuando se elimina una fila, debe utilizar señales.Usted necesita para eliminar el archivo real en tanto
delete
yupdate
.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)
fuente
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 parecía apropiado porque tenía un modelo de archivo dedicado para administrar todos mis archivos.
Nota: Por alguna razón, post_delete no parece funcionar correctamente. El archivo se eliminó, pero el registro de la base de datos se mantuvo, lo cual es completamente lo contrario de lo que esperaría, incluso en condiciones de error. pre_delete funciona bien.
fuente
post_delete
no funcione, porquefile_field.delete()
de forma predeterminada guarda el modelo en la base de datos, pruebefile_field.delete(False)
docs.djangoproject.com/en/1.3/ref/models/fields/…Quizás sea un poco tarde. Pero la forma más fácil para mí es usar una señal post_save. Solo para recordar que las señales se ejecutan incluso durante un proceso de eliminación de QuerySet, pero el método [modelo] .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 / signal.py
import os def delete_image_slide(sender, **kwargs): slide = kwargs.get('instance') try: os.remove(slide.image.path) except: pass
fuente
Esta funcionalidad se eliminará en Django 1.3, por lo que no confiaría en ella.
Puede anular el
delete
método del modelo en cuestión para eliminar el archivo antes de eliminar completamente la entrada de la base de datos.Editar:
He aquí 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)
fuente
MyModel.objects.all()[0].delete()
, eliminará el archivo mientrasMyModel.objects.all().delete()
que no. Usa señales.Usar post_delete es sin duda el camino correcto. A veces, las cosas pueden salir mal y los archivos no se eliminan. Por supuesto, existe el caso de que tenga un montón de archivos antiguos que no se eliminaron antes de que se usara 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 elimine el objeto, si el archivo no tiene un objeto, luego también elimine, también puede eliminar basado en una bandera "activa" para un objeto .. Algo que agregué a la mayoría de mis modelos. Tienes que pasarle los objetos que quieres comprobar, la ruta a los archivos de objetos, el campo del archivo y una bandera para borrar los 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
fuente
asegúrese de escribir " self " antes del archivo. así que el ejemplo anterior debería ser
def delete(self, *args, **kwargs): self.somefile.delete() super(MyModel, self).delete(*args, **kwargs)
He olvidado el "yo" antes de mi archivo y eso no funcionó ya que estaba buscando en el espacio de nombres global.
fuente
Si ya tiene una cantidad de archivos sin usar en su proyecto y desea eliminarlos, puede usar la utilidad django django-unused-media
fuente
Solución Django 2.x:
¡No es necesario instalar ningún paquete! Es muy fácil de manejar en Django 2 . Intenté seguir la solución usando Django 2 y SFTP Storage (sin embargo, creo que funcionaría con cualquier almacenamiento)
Primero escriba un administrador personalizado . Por lo tanto, si desea poder eliminar archivos de un modelo mediante
objects
métodos, debe escribir y usar un [Administrador personalizado] [3] (para reemplazar eldelete()
método deobjects
):class CustomManager(models.Manager): def delete(self): for obj in self.get_queryset(): obj.delete()
Ahora debe eliminar
image
antes de eliminar eliminando el modelo en sí y para asignar elCustomManager
al modelo, debe poner sus inicialesobjects
dentro de su modelo: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()
fuente
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 dinámico, 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)
fuente