1

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?

Dzejkob
  • 2,302
  • 2
  • 15
  • 20
  • Can you post the contents of your resize_image method? It seems like you could use some of the logic there that determines the final size of your image to set the banner_width and banner_height properties before saving, unless there is something magic going on in that method. – Casey Kinsey Mar 07 '12 at 22:39
  • @CaseyKinsey I updated this function recently to return resized image, and right now in `save` method I firstly call `super(SomeModel, self).save()` then do `resize_image()`, update `banner_width` and `banner_height`, and finally call `super(SomeModel, self).save()` again. It works, although I'd like to avoid saving model instance twice. – Dzejkob Mar 08 '12 at 09:11

1 Answers1

3

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.

Dzejkob
  • 2,302
  • 2
  • 15
  • 20