Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Replacing a Django image doesn't delete original

In Django, if you have a ImageFile in a model, deleting will remove the associated file from disk as well as removing the record from the database.

Shouldn't replacing an image also remove the unneeded file from disk? Instead, I see that it keeps the original and adds the replacement.

Now deleting the object won't delete the original file only the replacement.

Are there any good strategies to doing this? I don't want to have a bunch of orphan files if my users replace their images frequently.

like image 390
Shige Abe Avatar asked Dec 09 '10 02:12

Shige Abe


3 Answers

The best strategy I've found is to make a custom save method in the model:

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)

And beware, as with the updating which doesn't delete the back end file, deleting an instance model (here Photo) will not delete the back-end file, not in Django 1.3 anyway, you'll have to add more custom code to do that (or regularly do some dirty cron job).

Finally test all your update/delete cases with your ForeignKey, ManytoMany and others relations to check if the back-end files are correctly deleted. Believe only what you test.

like image 51
Geoffroy CALA Avatar answered Nov 07 '22 04:11

Geoffroy CALA


Shouldn't replacing an image also remove the unneeded file from disk?

In the olden days, FileField was eager to clean up orphaned files. But that changed in Django 1.2:

In earlier Django versions, when a model instance containing a FileField was deleted, FileField took it upon itself to also delete the file from the backend storage. This opened the door to several potentially serious data-loss scenarios, including rolled-back transactions and fields on different models referencing the same file. In Django 1.2.5, FileField will never delete files from the backend storage.

like image 15
Filip Dupanović Avatar answered Nov 07 '22 04:11

Filip Dupanović


The code in the following working example will, upon uploading an image in an ImageField, detect if a file with the same name exists, and in that case, delete that file before storing the new one.

It could easily be modified so that it deletes the old file regardless of the filename. But that's not what I wanted in my project.

Add the following class:

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

And use it with ImageField like so:

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)
like image 9
thnee Avatar answered Nov 07 '22 03:11

thnee