videos unique_together type how files example python django imagefield

python - unique_together - models filefield example



Reemplazar una imagen Django no elimina el original (7)

¿El reemplazo de una imagen no debería eliminar también el archivo innecesario del disco?

En los viejos tiempos, FileField estaba ansioso por limpiar archivos huérfanos. Pero eso cambió en Django 1.2 :

En versiones anteriores de Django, cuando se eliminaba una instancia de modelo que contenía un FileField, FileField se encargó de eliminar también el archivo del almacenamiento de back-end. Esto abrió la puerta a varios escenarios de pérdida de datos potencialmente graves, que incluyen transacciones retrotraídas y campos en diferentes modelos que hacen referencia al mismo archivo. En Django 1.2.5, FileField nunca eliminará archivos del almacenamiento de fondo.

En Django, si tiene un ImageFile en un modelo, eliminar eliminará el archivo asociado del disco y eliminará el registro de la base de datos.

¿El reemplazo de una imagen no debería eliminar también el archivo innecesario del disco? En cambio, veo que mantiene el original y agrega el reemplazo.

Ahora, eliminar el objeto no eliminará el archivo original solo el reemplazo.

¿Hay alguna buena estrategia para hacer esto? No quiero tener un montón de archivos huérfanos si mis usuarios reemplazan sus imágenes con frecuencia.


Aquí hay un código que puede funcionar con o sin upload_to=... o blank=True , y cuando el archivo enviado tiene el mismo nombre que el anterior.

(sintaxis py3, probado en Django 1.7)

class Attachment(models.Model): document = models.FileField(...) # or ImageField def delete(self, *args, **kwargs): self.document.delete(save=False) super().delete(*args, **kwargs) def save(self, *args, **kwargs): if self.pk: old = self.__class__._default_manager.get(pk=self.pk) if old.document.name and (not self.document._committed or not self.document.name): old.document.delete(save=False) super().save(*args, **kwargs)

Recuerde que este tipo de solución solo es aplicable si se encuentra en un contexto no transaccional (no hay reversión, porque el archivo se pierde definitivamente)


El código del siguiente ejemplo de trabajo, al cargar una imagen en un ImageField, detectará si existe un archivo con el mismo nombre y, en ese caso, eliminará ese archivo antes de almacenar el nuevo.

Se podría modificar fácilmente para que elimine el archivo antiguo independientemente del nombre del archivo. Pero eso no es lo que quería en mi proyecto.

Agregue la siguiente clase:

from django.core.files.storage import FileSystemStorage class OverwriteStorage(FileSystemStorage): def _save(self, name, content): if self.exists(name): self.delete(name) return super(OverwriteStorage, self)._save(name, content) def get_available_name(self, name): return name

Y úsalo con ImageField así:

class MyModel(models.Model): myfield = models.ImageField( ''description of purpose'', upload_to=''folder_name'', storage=OverwriteStorage(), ### using OverwriteStorage here max_length=500, null=True, blank=True, height_field=''height'', width_field=''width'' ) height = models.IntegerField(blank=True, null=True) width = models.IntegerField(blank=True, null=True)


Ha habido varios tickets relacionados con este problema, aunque es probable que no se convierta en el núcleo. El más completo es http://code.djangoproject.com/ticket/11663 . Los parches y los comentarios de los boletos pueden darle alguna dirección si está buscando una solución.

También puede considerar usar un StorageBackend diferente, como el Sistema de almacenamiento de archivos sobrescrito dado por el fragmento 976 de Django. http://djangosnippets.org/snippets/976/ . Puede cambiar su almacenamiento predeterminado a este backend o puede anularlo en cada declaración FileField / ImageField.


La mejor estrategia que he encontrado es crear un método de guardado personalizado en el modelo:

class Photo(models.Model): image = ImageField(...) # works with FileField also def save(self, *args, **kwargs): # delete old file when replacing by updating the file try: this = Photo.objects.get(id=self.id) if this.image != self.image: this.image.delete(save=False) except: pass # when new photo then we do nothing, normal case super(Photo, self).save(*args, **kwargs)

Y tenga cuidado, ya que con la actualización que no elimina el archivo final, la eliminación de un modelo de instancia (aquí Foto) no eliminará el archivo final, no de todos modos en Django 1.3, tendrá que agregar más código personalizado a hacer eso (o regularmente hacer algún trabajo cron sucio).

Finalmente, pruebe todos sus casos de actualización / eliminación con ForeignKey, ManytoMany y otras relaciones para verificar si los archivos de back-end se eliminan correctamente. Cree solo lo que pruebas .


Si no usa transacciones o no teme perder archivos en la reversión de transacciones, puede usar django-cleanup


Utilicé un método sencillo con popen , así que cuando popen mi modelo de Info borro el archivo anterior antes de vincularlo al nuevo:

import os try: os.popen("rm %s" % str(info.photo.path)) except: #deal with error pass info.photo = nd[''photo'']