How to delete old image when update ImageField?

Question:

I’m using Django to create a stock photo site, I have an ImageField in my model, the problem is that when the user updates the image field, the original image file isn’t deleted from the hard disk.

How can I delete the old images after an update?

Asked By: eos87

||

Answers:

You’ll have to delete the old image manually.

The absolute path to the image is stored in your_image_field.path. So you’d do something like:

os.remove(your_image_field.path)

But, as a convenience, you can use the associated FieldFile object, which gives easy access to the underlying file, as well as providing a few convenience methods. See http://docs.djangoproject.com/en/dev/ref/models/fields/#filefield-and-fieldfile

Answered By: Chris Lawlor

Use this custom save method in your model:

def save(self, *args, **kwargs):
    try:
        this = MyModelName.objects.get(id=self.id)
        if this.MyImageFieldName != self.MyImageFieldName:
            this.MyImageFieldName.delete()
    except: pass
    super(MyModelName, self).save(*args, **kwargs)

It works for me on my site. This problem was bothering me as well and I didn’t want to make a cleanup script instead over good bookkeeping in the first place. Let me know if there are any problems with it.

Answered By: metamemetics

Use django-cleanup

pip install django-cleanup

settings.py

INSTALLED_APPS = (
    ...
    'django_cleanup.apps.CleanupConfig', # should be placed after your apps

)
Answered By: un1t

Before updating the model instance, you can use the delete method of FileField object. For example, if the FileField or ImageField is named as photo and your model instance is profile, then the following will remove the file from disk

profile.photo.delete(False)

For more clarification, here is the django doc

https://docs.djangoproject.com/en/1.11/ref/models/fields/#django.db.models.fields.files.FieldFile.delete

Answered By: Nayan

Here is an app that deletes orphan files by default: django-smartfields.
It will remove files whenever:

  • field value was replaced with a new one (either uploaded or set manually)
  • field is cleared through the form (in case that field is not required, of course)
  • the model instance itself containing the field is deleted.

It is possible to turn that cleanup feature off using an argument: ImageField(keep_orphans=True) on per field basis, or globally in settings SMARTFIELDS_KEEP_ORPHANS = True.

from django.db import models

from smartfields import fields

class MyModel(models.Model):
    image = fields.ImageField()
    document = fields.FileField()
Answered By: lehins

You can define pre_save reciever in models:

@receiver(models.signals.pre_save, sender=UserAccount)
def delete_file_on_change_extension(sender, instance, **kwargs):
    if instance.pk:
        try:
            old_avatar = UserAccount.objects.get(pk=instance.pk).avatar
        except UserAccount.DoesNotExist:
            return
        else:
            new_avatar = instance.avatar
            if old_avatar and old_avatar.url != new_avatar.url:
                old_avatar.delete(save=False)

My avatrs has unique url for each person like “avatars/ceb47779-8833-4719-8711-6f4e5cabb2b2.png”. If user upload new image with different extension like jpg, delete_file_on_change_extension reciever remove old image, before save new with url “avatars/ceb47779-8833-4719-8711-6f4e5cabb2b2.jpg” (in this case). If user uploads new image with same extension django overwrite old image on storage (disk), because images paths are the same.
This works fine with AWS S3 django-storage.

Answered By: QuadX

Completing Chris Lawlor’s answer, tried this and works.

from YOURAPP.settings import BASE_DIR

try:
    os.remove(BASE_DIR + user.userprofile.avatarURL)
except Exception as e:
    pass 

The URL has a pattern of /media/mypicture.jpg

Answered By: mrj

try this, it will work even if old file is deleted

def logo_file(instance, filename):

    try:
        this = business.objects.get(id=instance.id)
        if this.logo is not None:
            path = "%s" % (this.logo)
            os.remove(path)
    finally:
        pass..

code will work even without “try .. finally” but it will generate problem if file was accidently deleted.
changed: move model matching inside “try” so it will not throw any error at user signup
Let me know if there are any problems.

Answered By: Swapnil Rane

What I did is saving the path to the old image and if form is valid I would delete the old one.

if request.method == 'POST':
        old_image = ""
        if request.user.profile.profile_picture:
            old_image = request.user.profile.profile_picture.path
        form = UpdateProfileForm(request.POST,request.FILES,instance = profile)
        if form.is_valid():
            if os.path.exists(old_image):
                os.remove(old_image)
        form.save()

It is a little messy , but you do not install third parties or anythin

Answered By: Martin Yanchev
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.