Idea for Spec Field which receives references to other model fields (Redo)

30 views
Skip to first unread message

Nina Pavlich

unread,
May 12, 2015, 12:23:36 PM5/12/15
to django-...@googlegroups.com
Hello,

(This is my first time contributing to a forum -- so just let me know if I'm doing this wrong! Sorry about the duplicate post, the editor got a little buggy on my the first found and I accidentally posted the message before it was complete.)

I have the following use-cases:
- Admins would like to upload an image, and choose how the image gets cropped for each of the image variants.
- Admins would like to upload an image, and choose how a watermark is positioned / scaled on the image. 

What I would love is to be able to use a special kind of spec field and processor which receives a reference to the model instance itself -- not just the image -- so it could combine values from other model fields.

Let me walk you through an example I've been working on: An image with a custom crop.

#models.py
image
= models.ImageField(blank=True, null=True)

square_150_crop_properties
= { 'source':'image', 'crop_field':'square_150_crop', 'resize_method':'fill', 'width':150, 'height':150, 'upscale':False }

square_150_crop
= ImageCropField(null=True, blank=True, properties=square_150_crop_properties)

square_150
= InstanceSpecField( source='image', instance_processors=[PositionCrop(square_150_crop_properties)], hash_key_values=['square_150_crop'])


We're using a custom processor to which we pass custom settings. The processor function has been modified to receive both the image value *and* a reference to the model instance itself:

#processors.py
class PositionCrop(object):
  def __init__(self, options):        
    self.options = options        
    self.crop_position_field = options['crop_field']        
    self.resize_method = options['resize_method']        
    self.width = options['width']        
    self.height = options['height']        
    self.upscale = options['upscale'] or False


  def process(self, image, instance):
 
    #Step 1, crop based on crop position
    crop_value
= getattr(instance, self.crop_position_field)
    original_width
= image.size[0]
    original_height
= image.size[1]




In addition, it will need a special hashing/picking function since we now trigger a change to the variant not just based on the image value, but the crop value in this case. 

#InstanceSpecField
def get_hash(self):
       
    keys
= [
       
self.source.name,
       
self.field.processors,
       
self.field.instance_processors,
       
self.field.format,
       
self.field.options,
       
self.autoconvert,
   
]
    instance
= self.instance
   
   
#Use the actual values of the fields to hash the instance
   
#REQUIRES INSTANCE:
   
for extra_field in self.field.extra_hash_key_values:
        field
= getattr(self.instance, extra_field)
        field_hash
= "%s_%s"%(extra_field, field)
        keys
.append(field_hash)


   
return hashers.pickle(keys)



I've created a working version of this (https://github.com/ninapavlich/django-imagekit-cropper), but it is essentially a big, ugly Monkey Patch. It would be safer to integrate something like this into the core functionality.

My questions are:
1) Is there a good solution that already exists that would accomplish the use cases above?
2) If not, does this approach seem good and that it would be helpful to others?
3) If this seems like a good solution, what are the steps I should take to get this integrated into the core?

Nina Pavlich

unread,
May 12, 2015, 1:50:28 PM5/12/15
to django-...@googlegroups.com
Doh! I see someone has already asked this same question here:

matthewwithanm

unread,
May 13, 2015, 9:56:02 AM5/13/15
to django-...@googlegroups.com, ni...@cgpartnersllc.com
I know I already responded to the other thread but I just had a chance to check out your project and it's super exciting! I think you'll be able to simplify things if you just set the processors in the constructor, but being able to do this kind of editorial stuff in the admin with IK would be awesome.

Nina Pavlich

unread,
May 13, 2015, 1:11:22 PM5/13/15
to django-...@googlegroups.com, ni...@cgpartnersllc.com
Thank you so much for the feedback! I'm really relived to see there's such an easier way to go about this. The current version will surely break after any internal change. 

The ability to choose the image crop -- especially for square crops -- is something our clients have been hankering for. And currently I'm working with a photographer that watermarks her images, so it'll make more sense for her if she can place the watermark within the crop area.

Imagekit is an awesome library to work with! Thank you for sharing it with everyone!

matthewwithanm

unread,
May 14, 2015, 10:30:51 AM5/14/15
to django-...@googlegroups.com, ni...@cgpartnersllc.com
Aw, thanks! And thanks for building django-imagekit-cropper. It's so cool to see other things—particularly reusable things—built on top of IK.

I wrote a comment on GitHub once that I always thought was a pretty good primer of the core IK primitives. (It really should be put into the docs at some point but I'm always so busy.) This probably isn't any news to you since you've obviously dug in pretty deeply already, but maybe it'll at least formalize it. In short, processors, generators, and specs are all glorified functions.

processors: (pil image) -> pil image
generators : (anything) -> image file
specs: (image file, anything else) -> image file

Editorial-time spec configuration is something I haven't played with much, but did think about when pulling out these primitives. My rough idea was that it would be handled with ImageSpec subclasses. As of yet, I don't think anybody has done this though! So there are probably ways that IK could be improved to make it easier…for example, I haven't thought about how you would make a subclass of an ImageSpecField that used a custom ImageSpec.

Anyway, I should add django-imagekit-cropper to the IK readme (like Instakit).  Do you want me to hold off until the API settles down?

Nina Pavlich

unread,
May 20, 2015, 5:15:28 PM5/20/15
to django-...@googlegroups.com, ni...@cgpartnersllc.com
Your suggestions were really helpful. I folded them into the code and was able to remove a lot of the hacks!

That would great to include it, but maybe with a warning that it's a pretty new library -- hasn't been tested very thoroughly. On the other hand, people using it would be a good way to find all the bugs. :)
Reply all
Reply to author
Forward
0 new messages