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.
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.
Respuestas:
Puede recibir la señal pre_delete
o post_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)
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 los ImageField(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ñales post_delete
y las pre_delete
señales es incorrecta.
Prueba django-cleanup
pip install django-cleanup
settings.py
INSTALLED_APPS = (
...
'django_cleanup', # should go after your apps
)
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 el ImageField
de mi Photo
modelo.
NotImplementedError: This backend doesn't support absolute paths.
Puede solucionar esto fácilmente pasando el nombre del campo del archivo a en storage.delete()
lugar de la ruta del campo del archivo. Por ejemplo, reemplace las dos últimas líneas de esta respuesta con storage, name = photo.original_image.storage, photo.original_image.name
entonces storage.delete(name)
.
mymodel.myimagefield.delete(save=False)
en su lugar. docs.djangoproject.com/en/dev/ref/files/file/…
mymodel.myimagefield.delete(save=False)
en post_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 hace instance.myimagefield.delete(save=False)
, tenga en cuenta el uso de instance
.
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.
delete
no siempre se llama cuando se elimina una fila, debe utilizar señales.
Usted necesita para eliminar el archivo real en tanto delete
y 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 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.
post_delete
no funcione, porque file_field.delete()
de forma predeterminada guarda el modelo en la base de datos, pruebe file_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
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)
MyModel.objects.all()[0].delete()
, eliminará el archivo mientras MyModel.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
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.
Si ya tiene una cantidad de archivos sin usar en su proyecto y desea eliminarlos, puede usar la utilidad django django-unused-media
¡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 el delete()
método de objects
):
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 el CustomManager
al modelo, debe poner sus iniciales objects
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()
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)