Estoy construyendo una aplicación web en Django. Tengo un modelo que sube un archivo, pero no puedo borrarlo. Aquí está mi código:
class Song(models.Model):
name = models.CharField(blank=True, max_length=100)
author = models.ForeignKey(User, to_field='id', related_name="id_user2")
song = models.FileField(upload_to='/songs/')
image = models.ImageField(upload_to='/pictures/', blank=True)
date_upload = models.DateField(auto_now_add=True)
def delete(self, *args, **kwargs):
# You have to prepare what you need before delete the model
storage, path = self.song.storage, self.song.path
# Delete the model before the file
super(Song, self).delete(*args, **kwargs)
# Delete the file after the model
storage.delete(path)
Luego, en "python manage.py shell" hago esto:
song = Song.objects.get(pk=1)
song.delete()
Elimina de la base de datos pero no el archivo en el servidor. ¿Qué más puedo probar?
¡Gracias!
django
django-models
Marcos Aguayo
fuente
fuente
Respuestas:
Antes de Django 1.3, el archivo se eliminaba del sistema de archivos automáticamente cuando eliminaba la instancia del modelo correspondiente. Probablemente esté utilizando una versión más reciente de Django, por lo que tendrá que implementar la eliminación del archivo del sistema de archivos usted mismo.
Puede hacerlo de varias formas, una de las cuales es mediante una señal
pre_delete
opost_delete
.Ejemplo
Mi método de elección actualmente es una combinación de
post_delete
ypre_save
señales, lo que hace que los archivos obsoletos se eliminen cada vez que se eliminan los modelos correspondientes o se modifican sus archivos.Basado en un
MediaFile
modelo hipotético :import os import uuid from django.db import models from django.dispatch import receiver from django.utils.translation import ugettext_lazy as _ class MediaFile(models.Model): file = models.FileField(_("file"), upload_to=lambda instance, filename: str(uuid.uuid4())) # These two auto-delete files from filesystem when they are unneeded: @receiver(models.signals.post_delete, sender=MediaFile) def auto_delete_file_on_delete(sender, instance, **kwargs): """ Deletes file from filesystem when corresponding `MediaFile` object is deleted. """ if instance.file: if os.path.isfile(instance.file.path): os.remove(instance.file.path) @receiver(models.signals.pre_save, sender=MediaFile) def auto_delete_file_on_change(sender, instance, **kwargs): """ Deletes old file from filesystem when corresponding `MediaFile` object is updated with new file. """ if not instance.pk: return False try: old_file = MediaFile.objects.get(pk=instance.pk).file except MediaFile.DoesNotExist: return False new_file = instance.file if not old_file == new_file: if os.path.isfile(old_file.path): os.remove(old_file.path)
save()
(por ejemplo, mediante una actualización masiva aQuerySet
), el archivo antiguo seguirá ahí porque las señales no se ejecutarán. Esto no sucede si utiliza métodos de manejo de archivos convencionales.file
como nombre de campo, que no es un buen estilo porque choca con elfile
identificador de objeto incorporado .Ver también
FieldFile.delete()
en la referencia de campo del modelo Django 1.11 (tenga en cuenta que describe laFieldFile
clase, pero llamaría.delete()
directamente en el campo:FileField
proxy de instancia a laFieldFile
instancia correspondiente , y accede a sus métodos como si fueran de campo)Por qué Django no elimina archivos automáticamente: entrada en las notas de la versión para Django 1.3
Ejemplo de uso de una
pre_delete
sola señalfuente
instance.song.delete(save=False)
, ya que usa el motor de almacenamiento correcto de django.os.path.isfile(old_file.path)
falla porqueold_file.path
genera un error (no hay ningún archivo asociado con el campo). Lo arreglé agregandoif old_file:
justo antes de la llamada aos.path.isfile()
.Pruebe django-cleanup , automáticamente invoca el método de eliminación en FileField cuando elimina el modelo.
pip install django-cleanup
settings.py
INSTALLED_APPS = ( ... 'django_cleanup', # should go after your apps )
fuente
Puede eliminar un archivo del sistema de archivos con el
.delete
método de llamada del campo de archivo que se muestra a continuación con Django> = 1.10:obj = Song.objects.get(pk=1) obj.song.delete()
fuente
También puede simplemente sobrescribir la función de eliminación del modelo para verificar el archivo si existe y eliminarlo antes de llamar a la superfunción.
import os class Excel(models.Model): upload_file = models.FileField(upload_to='/excels/', blank =True) uploaded_on = models.DateTimeField(editable=False) def delete(self,*args,**kwargs): if os.path.isfile(self.upload_file.path): os.remove(self.upload_file.path) super(Excel, self).delete(*args,**kwargs)
fuente
queryset.delete()
no limpiarán los archivos con esta solución. Debería iterar sobre el conjunto de consultas y llamar.delete()
a cada objeto.Solución Django 2.x:
Es muy fácil manejar la eliminación de archivos en Django 2 . Intenté seguir la solución usando Django 2 y SFTP Storage y también FTP STORAGE, y estoy bastante seguro de que funcionará con cualquier otro administrador de almacenamiento que haya implementado el
delete
método. (eldelete
método es uno de losstorage
métodos abstractos).Anule el
delete
método del modelo de forma que la instancia elimine sus FileFields antes de borrarse a sí misma:class Song(models.Model): name = models.CharField(blank=True, max_length=100) author = models.ForeignKey(User, to_field='id', related_name="id_user2") song = models.FileField(upload_to='/songs/') image = models.ImageField(upload_to='/pictures/', blank=True) date_upload = models.DateField(auto_now_add=True) def delete(self, using=None, keep_parents=False): self.song.storage.delete(self.song.name) self.image.storage.delete(self.song.name) super().delete()
Funciona bastante fácil para mí. Si desea verificar si el archivo existe antes de eliminarlo, puede usar
storage.exists
. por ejemploself.song.storage.exists(self.song.name)
, devolverá unaboolean
representación si la canción existe. Entonces se verá así:def delete(self, using=None, keep_parents=False): # assuming that you use same storage for all files in this model: storage = self.song.storage if storage.exists(self.song.name): storage.delete(self.song.name) if storage.exists(self.image.name): storage.delete(self.song.name) super().delete()
EDITAR (además):
Como mencionó @HeyMan , con esta solución, la llamada
Song.objects.all().delete()
no elimina archivos. Esto sucede porque seSong.objects.all().delete()
está ejecutando la consulta de eliminación del Administrador predeterminado . Entonces, si desea poder eliminar archivos de un modelo mediante el uso deobjects
métodos, debe escribir y usar un Administrador personalizado (solo para anular su consulta de eliminación):class CustomManager(models.Manager): def delete(self): for obj in self.get_queryset(): obj.delete()
y para asignar el
CustomManager
al modelo, debe poner sus inicialesobjects
dentro de su modelo:class Song(models.Model): name = models.CharField(blank=True, max_length=100) author = models.ForeignKey(User, to_field='id', related_name="id_user2") song = models.FileField(upload_to='/songs/') image = models.ImageField(upload_to='/pictures/', blank=True) date_upload = models.DateField(auto_now_add=True) objects = CustomManager() # just add this line of code inside of your model def delete(self, using=None, keep_parents=False): self.song.storage.delete(self.song.name) self.image.storage.delete(self.song.name) super().delete()
Ahora puede usar
.delete()
al final de cualquierobjects
subconsulta. Escribí lo más simpleCustomManager
, pero puedes hacerlo mejor devolviendo algo sobre los objetos que eliminaste o lo que quieras.fuente
Aquí hay una aplicación que eliminará archivos antiguos cada vez que se elimine el modelo o se cargue un archivo nuevo: django-smartfields
from django.db import models from smartfields import fields class Song(models.Model): song = fields.FileField(upload_to='/songs/') image = fields.ImageField(upload_to='/pictures/', blank=True)
fuente
@Anton Strogonoff
Me falta algo en el código cuando cambia un archivo, si crea un archivo nuevo genera un error, porque es un archivo nuevo y no encontró una ruta. Modifiqué el código de función y agregué una oración try / except y funciona bien.
@receiver(models.signals.pre_save, sender=MediaFile) def auto_delete_file_on_change(sender, instance, **kwargs): """Deletes file from filesystem when corresponding `MediaFile` object is changed. """ if not instance.pk: return False try: old_file = MediaFile.objects.get(pk=instance.pk).file except MediaFile.DoesNotExist: return False new_file = instance.file if not old_file == new_file: try: if os.path.isfile(old_file.path): os.remove(old_file.path) except Exception: return False
fuente
try:
bloque (AttributeError
¿quizás?).Este código se ejecutará cada vez que subo una nueva imagen (campo del logotipo) y verifico si ya existe un logotipo, si es así, ciérrelo y elimínelo del disco. Por supuesto, se podría realizar el mismo procedimiento en la función de receptor. Espero que esto ayude.
# Returns the file path with a folder named by the company under /media/uploads def logo_file_path(instance, filename): company_instance = Company.objects.get(pk=instance.pk) if company_instance.logo: logo = company_instance.logo if logo.file: if os.path.isfile(logo.path): logo.file.close() os.remove(logo.path) return 'uploads/{0}/{1}'.format(instance.name.lower(), filename) class Company(models.Model): name = models.CharField(_("Company"), null=False, blank=False, unique=True, max_length=100) logo = models.ImageField(upload_to=logo_file_path, default='')
fuente