3

I am looking for a way to resize, compress, and optimize the uploaded image when saving an ImageField.

class Image(models.Model):
    name = models.CharField(max_length=254, blank=True)
    caption = models.TextField(max_length=1000, blank=True)
    height = models.IntegerField()
    width = models.IntegerField()
    image = models.ImageField(upload_to='', height_field='height', width_field='width', storage=S3MediaStorage())

My first thought was to override the model's save() and implement this logic there, but I don't want the resize/compression/optimization to run again if the user doesn't update the image file (i.e. if he only updates name or caption on an existing object and saves it).

  1. What is a proper way to check when a new image file is uploaded to the ImageField, but not when the user only changes another field in the Model, eg. the user updates caption but leaves everything else as-is?

  2. How can the uploaded image file be accessed in code? I.e. what is the variable that contains the actual image file that can be passed to Pillow?

edit: This is unique from the suspected duplicate. I am not asking if the field has changed, because that would always cause false positives. I am asking if the user has uploaded an image file, which I will immediately change (resize/optimize/compress), so if the user immediately downloads his uploaded image he'll find that has a different binary with a randomly generated filename, and therefore comparing the filename or binary are not valid methods to determine if the user is uploading a different image.

Community
  • 1
  • 1
davidtgq
  • 3,780
  • 10
  • 43
  • 80
  • Possible duplicate of [Django: When saving, how can you check if a field has changed?](http://stackoverflow.com/questions/1355150/django-when-saving-how-can-you-check-if-a-field-has-changed) – rnevius Mar 21 '16 at 14:14
  • @rnevius I've added information to differentiate this question, thanks – davidtgq Mar 21 '16 at 19:45

1 Answers1

1

Your model could use a different name.

Nevertheless, you can try manipulating the image through a post_save signal (https://docs.djangoproject.com/en/1.9/ref/signals/#post-save)

from PIL import Image
from django.db.models.signals import post_save

@receiver(post_save, sender=Image)
def crop_image(sender, instance, **kwargs):
    img = instance.image
    original = Image.open(img.src.path)
    # ... your code here...

EDIT: Apologies. Jumped the gun a bit. One of your actual problems was to not manipulate the image if it's the same. You can do it on save() like this (UNTESTED):

def save(self, **kwargs):
    try:
        related_img = Image.objects.get(id=self.id)
        if related_img.image != self.image:
            crop_me(self.image)
    except Image.DoesNotExist:
        # object doesn't exist. Passing...
        pass

    return super(Image, self).save(**kwargs)

def crop_me(img):
    original_img = Image.open(img.src.path)
    # ... your code here...

EDIT 2: If the name changes you could save the original filename in an helper field

class Image(models.Model):
    image = models.ImageField(upload_to='', height_field='height', width_field='width', storage=S3MediaStorage())

    __original_image_filename = None

    def __init__(self, *args, **kwargs):
        super(Image, self).__init__(*args, **kwargs)
        self.__original_image_filename = self.image.name

    def save(self, force_insert=False, force_update=False, *args, **kwargs):
        if self.image.name != self.__original_image_filename:
        # name changed - do something here

        super(Image, self).save(force_insert, force_update, *args, **kwargs)
        self.__original_image_filename = self.image.name

I am modifying another answer on the fly so there could be an error or two. Please check the original answer. There are other methods on that question that could help you.

Community
  • 1
  • 1
Mærcos
  • 188
  • 14
  • 1
    You wrote `if not self.id` and then inside `if` block, you're doing `id=self.id`. Might want fix that. It should be `if self.id`. – xyres Mar 21 '16 at 14:59
  • Fixed. Thanks for pointing that out! It's also antipattern (EAFP) http://docs.quantifiedcode.com/python-code-patterns/readability/asking_for_permission_instead_of_forgiveness_when_working_with_files.html – Mærcos Mar 21 '16 at 16:00
  • `if related_img.image != self.image:` This might work if I keep a copy of the original file uploaded in `related_img.image`, but it doesn't look like it'd work if I only save a copy of the original file that has been renamed to a random string and the image has been resized and compressed. Is it possible to just check if the user uploaded an image file? – davidtgq Mar 21 '16 at 19:43
  • @DavidTan please see EDIT 2 – Mærcos Mar 22 '16 at 09:18