'''
Grab all the code below
Then, you just need to create a model like this:
class MyModel(models.Model):
profile_img = CompressedImageField()
'''
from io import BytesIO
from django.core.files import File
from django.db.models.fields.files import ImageField, ImageFieldFile
from PIL import Image
class CompressedImageFieldFile(ImageFieldFile):
def save(self, name: str, content: File, save: bool = ...) -> None:
if not content.readable():
return
# open the file as PIL image
image = Image.open(content)
# set up an in-memory byte io interface
inmem_io = BytesIO()
# get width and format information
img_width, _ = image.size
img_format = image.format
# checking the width
if img_width > self.field.thumbnail_width:
# create the thumbnail
image.thumbnail((
int(self.field.thumbnail_width),
int(self.field.thumbnail_width)
), Image.LANCZOS)
# save the results to the in-memory file
image.save(inmem_io, img_format)
# change content to the new file
content = File(inmem_io, name=content.name)
return super().save(name, content, save=save)
class CompressedImageField(ImageField):
attr_class = CompressedImageFieldFile
def __init__(self, *args, thumbnail_width=500, **kwargs) -> None:
self.thumbnail_width = thumbnail_width
super().__init__(*args, **kwargs)
def deconstruct(self):
_name, _path, _args, kwargs = super().deconstruct()
self._set_extra_kwargs(kwargs)
return (_name, _path, _args, kwargs)
def _set_extra_kwargs(self, kwargs):
if self.thumbnail_width != 500:
kwargs['thumbnail_width'] = self.thumbnail_width