Brainstorm's snippets (1/258)

  django-imagekit: How to cleanup cache backend and remove generated thumbnails from storage

Best effort to regenerate the thumbnail when the associated image is updated

In a django Model, I keep an ImageField "image" and an associated "thumbnail", based on from imagekit.models.ImageSpecField.

I might need to rebuild the image, keeping the same filename for the bitmap.

Obviously, the thumbnail needs to be updated as well; my first attempt was:

self.thumbnail.generate(force=True)

but that's not enough; despite the force=True parameter, the thumbnail remains unchanged.

After some trials and errors, I ended up with the following workaround:

class MyModel(model.Model):

    ...

    image = models.ImageField(_('Image'), null=True, blank=True, upload_to=get_acquisition_media_file_path)
    thumbnail = ImageSpecField(source='image', processors=[ResizeToFill(200, 100)], format='PNG')

    ...

    def save_image_from_data(self):

        # Create a new random image
        from random import randint
        rgb = (randint(0, 255), randint(0, 255), randint(0, 255))
        image_size = (200, 200)
        image = PILImage.new('RGB', image_size, rgb)
        with io.BytesIO() as output:
            image.save(output, format="PNG")
            contents = output.getvalue()

        # Best effort to remove the thumbnail, if any
        try:
            file = self.thumbnail.file
        #except FileNotFoundError:
        except:
            pass
        try:
            cache_backend = self.thumbnail.cachefile_backend
            cache_backend.cache.delete(cache_backend.get_key(file))
            #self.thumbnail.storage.delete(file)
            self.thumbnail.storage.delete(file.name)
        except:
            pass

        # Save image
        self.image = ContentFile(contents, "acquisition_%d.png" % self.position)
        self.save()

        # Regenerate thumbnail
        self.thumbnail.generate(force=True)

        return True

Thumbnails cleanup

ImageKit has a bug where files are cached and not deleted right away:

https://github.com/matthewwithanm/django-imagekit/issues/229#issuecomment-315690575

An interesting solution to the is the following.

UNTESTED: I didn't try this yet in a real situation

from django.db.models.signals import post_delete

    @staticmethod
    def post_delete(sender, instance, **kwargs):
        for field in ['thumbnail', ]:
            field = getattr(instance, field)
            try:
                file = field.file
            except FileNotFoundError:
                pass
            else:
                cache_backend = field.cachefile_backend
                cache_backend.cache.delete(cache_backend.get_key(file))
                field.storage.delete(file)
        instance.file.delete(save=False)

post_delete.connect(MyModel.post_delete, sender=MyModel)

References

Manually clear the cache

because in ImageKit 4.0 django cache backend is configured to preserve the state of images forever then you need to clear it also.

from imagekit.utils import get_cache
get_cache().clear()