Reputation: 4010
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).
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?
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.
Upvotes: 3
Views: 3058
Reputation: 373
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.
Upvotes: 1