Dzejkob
Dzejkob

Reputation: 2442

ImageField resizing on save also updating width_field and height_field

I have a model containing ImageField which should be resized after uploading.

class SomeModel(models.Model):
    banner = ImageField(upload_to='uploaded_images',
                        width_field='banner_width',
                        height_field='banner_height')
    banner_width = models.PositiveIntegerField(_('banner width'), editable=False)
    banner_height = models.PositiveIntegerField(_('banner height'), editable=False)

def save(self, *args, **kwargs):
    super(SomeModel, self).save(*args, **kwargs)
    resize_image(filename=self.banner.path,
                 width=MAX_BANNER_WIDTH,
                 height=MAX_BANNER_HEIGHT)

resize_image is a custom function which does the resizing, and everything works fine, except that banner_width and banner_height are populated with dimensions of original image, before resizing.

Actual size of resized image may be smaller than MAX values given, so I have to open resized file to check it's actual dimensions after resizing. I could then manually set banner_width and banner_height, and save again, but it's not efficient way. I could also do the resizing first, set width and height fields, and then save, but file at location self.banner.path doesn't exist before save is performed.

Any suggestions on how should this be done properly?

Upvotes: 1

Views: 2121

Answers (1)

Dzejkob
Dzejkob

Reputation: 2442

After several hours of trying to do this efficiently, I've changed my approach to this problem and defined CustomImageField like this:

class CustomImageField(ImageField):
    attr_class = CustomImageFieldFile

    def __init__(self, resize=False, to_width=None, to_height=None, force=True, *args, **kwargs):
        self.resize = resize
        if resize:
            self.to_width = to_width
            self.to_height = to_height
            self.force = force
        super(CustomImageField, self).__init__(*args, **kwargs)


class CustomImageFieldFile(ImageFieldFile): 

    def save(self, name, content, save=True):
        super(CustomImageFieldFile, self).save(name, content, save=save)
        if self.field.resize:
            resized_img = resize_image(filename=self.path,
                                       width=self.field.to_width,
                                       height=self.field.to_height,
                                       force=self.field.force)
            if resized_img:
                setattr(self.instance, self.field.width_field, resized_img.size[0])
                setattr(self.instance, self.field.height_field, resized_img.size[1])

Now I can just define:

class SomeModel(models.Model):
    my_image = CustomImageField(resize=True, to_width=SOME_WIDTH, to_height=SOME_HEIGHT, force=False,
                                width_field='image_width', height_field='image_height')
    image_width = models.PositiveIntegerField(editable=False)
    image_height = models.PositiveIntegerField(editable=False)

And depending on resize argument, image can be automatically resized after uploading, and width/height fields are correctly updated, without saving object twice. After quick tests it seems to be working fine.

Upvotes: 3

Related Questions