1

I would like to resize and optimize image on save. But only on the first save of image, not on all save. Also I would like to rename file and automatically overwrite old file if new file is uploaded.

Goal is to

  • rename images to /menues/{shopid}.jpg (also change from png to jpg)
  • optimize image for web
  • rescale width and height if image to max 1400 width/height

Below is the code I have created untill now.

def get_upload_path(instance, filename):
    name, ext = 'menu', os.path.splitext(filename)[1]
    if instance.shopid:  # shopid is defined by form view right before create()
        return os.path.join("menues", instance.shopid + ext)
    return os.path.join('menues', filename)

def scale_dimensions(width, height, longest_side):
    if width > height:
        if width > longest_side:
            ratio = longest_side*1./width
            return (int(width*ratio), int(height*ratio))
    elif height > longest_side:
        ratio = longest_side*1./height
        return (int(width*ratio), int(height*ratio))
    return (width, height)

from PIL import Image
import os
MAX_MENU_IMG_MB = 10.0
IMG_RESIZE_LONGEST_SIDE = 1400

class Shop(models.Model):
    created_at = models.DateTimeField(auto_now_add=True)
    shopid = models.CharField(max_length=14, blank=True, editable=False)
    name = models.CharField()
    menu = models.ImageField(_("Photo (Max %sMB)") % str(MAX_MENU_IMG_MB), upload_to=get_upload_path, null=False, blank=False)

    def save(self):
        super(Shop, self).save()

        if self.menu:
            image = Image.open(self.menu.path)
            (width, height) = image.size
            (new_width, new_height) = scale_dimensions(width, height, longest_side=IMG_RESIZE_LONGEST_SIDE)
            if new_width < width or new_height < height:
                image = image.resize((new_width, new_height))
            image.save("%s.jpg" % self.menu.path.split('.')[0], format='JPEG', quality=70, optimize=True)
  • before the `super().save()` call, you can do `first_save = not self.id`. You can use first_save after super call to know it's the first save. – Ramkishore M Apr 16 '18 at 08:57

1 Answers1

0

Use init to see change of image

def __init__(self):
    super(Shop, self).__init__(*args, **kwargs)
    self.__previous_menu = self.menu if self.id else None

On save, remove previous image and the original version of new image

def save(self, *args, **kwargs):
    super().save(*args, **kwargs)
    previous_menu = self.__previous_menu
    if self.menu != previous_menu:
        # delete old image if changed
        if previous_menu:
            os.remove('{}/{}'.format(
                settings.MEDIA_ROOT,previous_menu.name).replace("\\","/"))

        org_file = self.menu
        # resize logic here

        self.__previous_menu = self.menu # to avoid infinite loop
        # remove original version of new image
        os.remove('{}/{}'.format(
            settings.MEDIA_ROOT,org_file.name).replace("\\","/"))
        self.save()
Ramkishore M
  • 359
  • 3
  • 7