django-imagekit icon indicating copy to clipboard operation
django-imagekit copied to clipboard

What is the preferred way to create (access) thumbnails from python code?

Open a1tus opened this issue 10 years ago • 5 comments

Hi. I've decided to clarify this aspect of imagekit usage. As far as I can see its not well described in docs.

To begin with, let's assume that we already have some model with ImageField and some custom ImageSpec class defined in imagegenerators.py for this app. At the moment we have a great way to generate and use thumbs from templates via generateimage tag. After passing generator id and ImageField to this tag we get thumb's url/with/height etc. So how should I get it in my view for example?

Here there's an information how to do get "file-like object containing our resized image, with which you can do whatever you want" but it doesn't not seem to be very useful: http://django-imagekit.readthedocs.org/en/latest/#defining-specs-outside-of-models

At the moment I'm doing smth like this:

from imagekit.cachefiles import ImageCacheFile
from .imagegenerators import SpecWithWatermark
spec = ImageCacheFile(SpecWithWatermark(model_obj.photo))
spec.generate()  # might be optional
url = spec.url

Is it ok there's a better approach? Also I would suggest to add a few lines in docs about this (at the moment its easy described only for specs defined in models).

a1tus avatar Mar 13 '15 05:03 a1tus

Sorry, I'm not understanding what you're asking. You're trying to get something in your view?

matthewwithanm avatar Mar 13 '15 06:03 matthewwithanm

Yeah, I just want to get the thumb itself (its url and dimensions) and be sure that it's rly generated. Like we do it via {% generateimage 'gen_id' source=src %} in templates.

An example of usecase is preparing json list of images for gallery. Or generating export file with some items and its photos (in my case I need to add watermark before sharing).

a1tus avatar Mar 13 '15 06:03 a1tus

Ah, yes, that is exactly how you would want to do it Python and I would love more docs about it!

Each piece in IK is meant to be able to be used alone but combine together to build the "higher level" things like the generateimage template tag.

"Image generators" is IK's name for objects that create (PIL) images. They're glorified functions of the form (*args, **kwargs) -> Image. You could make a perlin noise image generator, or one that generates a unique avatar from a username.

"Image specs" are a subset of generators. They create (PIL) images using a source image. In other words, glorified functions of the form (source: Image, *args, **kwargs) -> Image.

ImageCacheFiles are constructs built on top of the idea of image generators. They provide a file-like interface for lazily evaluating image generators and storing the result.

The generateimage template tag is built on top of ImageCacheFiles to expose an idiomatic Django interface for them in templates. Similarly, the model field ImageSpecField pulls together image specs and ImageCacheFiles in a way that's idiomatic for Django models although, unlike the template tag, it's solely for image specs (not generic generators).

matthewwithanm avatar Mar 13 '15 13:03 matthewwithanm

Nice to hear that I did it the right way :)

The only question left is what to do if we need dimensions (and must be sure that images are really generated) but use async backend as default one, because in this case as I undestand we would have access only to url (in fact we always can generate url using only ImageSpec.cachefile_name).

And vise versa: sometimes we may need to use async generation while default backend is simple (synchronous) in order to to get urls while not waiting for it in our script.

In this case I suppose we can use smth like this to be sure in consistent behaviour between different default settings:


from imagekit.cachefiles import backends, strategies, ImageCacheFile
from .imagegenerators import SpecWithWatermark

# force synchronous generation to access width/height
spec = ImageCacheFile(SpecWithWatermark(model_obj.photo),
    cachefile_backend=backends.Simple,
    cachefile_strategy=strategies.JustInTime,
)
# not need in spec.generate() as far as I can see, am I right?
url, w, h = spec.url, spec.width, spec.height

# force async generation to reduce script execution time
spec = ImageCacheFile(SpecWithWatermark(model_obj.photo),
    cachefile_backend=backends.Celery,
    cachefile_strategy=strategies.Optimistic,  # not sure we need it
)
url = spec.url  # dimensions are not available

I hope I'm not too meticulous :) There're a lot of small parts in IK and sometimes it's hard to "load" them all in head so I just want to specify all the details accurately to use them in future without worry. If you think it's useful I can try to create pull request to extend docs with this info.

a1tus avatar Mar 14 '15 21:03 a1tus

No such thing as too meticulous! And I would love more docs!

Unfortunately, the dimensions aren't (currently) cached, and there's no way to get them without actually generating the image (unlike the URL which can always be inferred from the spec without I/O). That means you can't really get them from the cache file when using an async backend. In the future, I'd like them to be cached as well.

I think you've got a very good grasp of how the small parts fit together! Hopefully most people won't need to use these small parts independently, but I wanted to build the larger parts from them so it would be possible. What you're doing in that example looks fine—though obviously the second ImageCacheFile wouldn't be too useful immediately after you had just created the first and forces a synchronous image creation. One little nitpick would be that I wouldn't call those things "specs." In IK3, "specs" define a way to generate an image from another. ImageCacheFiles are a kind of lazily-evaluated representation of that result image.

matthewwithanm avatar Mar 20 '15 00:03 matthewwithanm