Using Sizers and Filters

Where VersatileImageField shines is in its ability to create new images on the fly via its Sizer & Filter framework.

Sizers

Sizers provide a way to create new images of differing sizes from the one assigned to the field. VersatileImageField ships with two Sizers, thumbnail and crop.

Each Sizer registered to the Sizer registry is available as an attribute on each VersatileImageField. Sizers are dict subclasses that only accept precisely formatted keys comprised of two integers – representing width and height, respectively – separated by an ‘x’ (i.e. ['400x400']). If you send a malformed/invalid key to a Sizer, a MalformedSizedImageKey exception will raise.

Included Sizers

thumbnail

Here’s how you would create a thumbnail image that would be constrained to fit within a 400px by 400px area:

# Importing our example Model
>>> from someapp.models import ImageExampleModel
# Retrieving a model instance
>>> example = ImageExampleModel.objects.all()[0]
# Displaying the path-on-storage of the image currently assigned to the field
>>> example.image.name
u'images/testimagemodel/test-image.jpg'
# Retrieving the path on the field's storage class to a 400px wide
# by 400px tall constrained thumbnail of the image.
>>> example.image.thumbnail['400x400'].name
u'__sized__/images/testimagemodel/test-image-thumbnail-400x400.jpg'
# Retrieving the URL to the 400px wide by 400px tall thumbnail
>>> example.image.thumbnail['400x400'].url
u'/media/__sized__/images/testimagemodel/test-image-thumbnail-400x400.jpg'

Note

Images are created on-demand. If no image had yet existed at the location required – by either the path (.name) or URL (.url) shown in the highlighted lines above – one would have been created directly before returning it.

Here’s how you’d open the thumbnail image we just created as an image file directly in the shell:

>>> thumbnail_image = example.image.field.storage.open(
...     example.image.thumbnail['400x400'].name
... )

crop

To create images cropped to a specific size, use the crop Sizer:

# Retrieving the URL to a 400px wide by 400px tall crop of the image
>>> example.image.crop['400x400'].url
u'/media/__sized__/images/testimagemodel/test-image-crop-c0-5__0-5-400x400.jpg'

The crop Sizer will first scale an image down to its longest side and then crop/trim inwards, centered on the Primary Point of Interest (PPOI, for short). For more info about what PPOI is and how it’s used see the Specifying a Primary Point of Interest (PPOI) section.

How Sized Image Files are Named/Stored

All Sizers subclass from versatileimagefield.datastructures.sizedimage.SizedImage which uses a unique-to-size-specified string – provided via its get_filename_key() method – that is included in the filename of each image it creates.

Note

The thumbnail Sizer simply combines 'thumbnail' with the size key passed (i.e. '400x400' ) while the crop Sizer combines 'crop', the field’s PPOI value (as a string) and the size key passed; all Sizer ‘filename keys’ begin and end with dashes '-' for readability.

All images created by a Sizer are stored within the field’s storage class in a top-level folder named '__sized__', maintaining the same descendant folder structure as the original image. If you’d like to change the name of this folder to something other than '__sized__', adjust the value of VERSATILEIMAGEFIELD_SETTINGS['sized_directory_name'] within your settings file.

Sizers are quick and easy to write, for more information about how it’s done, see the Writing a Custom Sizer section.

Filters

Filters create new images that are the same size and aspect ratio as the original image.

Included Filters

invert

The invert filter will invert the color palette of an image:

# Importing our example Model
>>> from someapp.models import ImageExampleModel
# Retrieving a model instance
>>> example = ImageExampleModel.objects.all()[0]
# Returning the path-on-storage to the image currently assigned to the field
>>> example.image.name
u'images/testimagemodel/test-image.jpg'
# Displaying the path (within the field's storage class) to an image
# with an inverted color pallete from that of the original image
>>> example.image.filters.invert.name
u'images/testimagemodel/__filtered__/test-image__invert__.jpg'
# Displaying the URL to the inverted image
>>> example.image.filters.invert.url
u'/media/images/testimagemodel/__filtered__/test-image__invert__.jpg'

As you can see, there’s a filters attribute available on each VersatileImageField which contains all filters currently registered to the Filter registry.

Using Sizers with Filters

What makes Filters extra-useful is that they have access to all registered Sizers:

# Creating a thumbnail of a filtered image
>>> example.image.filters.invert.thumbnail['400x400'].url
u'/media/__sized__/images/testimagemodel/__filtered__/test-image__invert__-thumbnail-400x400.jpg'
# Creating a crop from a filtered image
>>> example.image.filters.invert.crop['400x400'].url
u'/media/__sized__/images/testimagemodel/__filtered__/test-image__invert__-c0-5__0-5-400x400.jpg'

Note

Filtered images are created the first time they are directly accessed (by either evaluating their name/url attributes or by accessing a Sizer attached to it). Once created, a reference is stored in the cache for each created image which makes for speedy subsequent retrievals.

How Filtered Image Files are Named/Stored

All Filters subclass from versatileimagefield.datastructures.filteredimage.FilteredImage which provides a get_filename_key() method that returns a unique-to-filter-specified string – surrounded by double underscores, i.e. '__invert__' – which is appended to the filename of each image it creates.

All images created by a Filter are stored within a folder named __filtered__ that sits in the same directory as the original image. If you’d like to change the name of this folder to something other than ‘filtered‘, adjust the value of VERSATILEIMAGEFIELD_SETTINGS['filtered_directory_name'] within your settings file.

Filters are quick and easy to write, for more information about creating your own, see the Writing a Custom Filter section.

Using Sizers / Filters in Templates

Template usage is straight forward and easy since both attributes and dictionary keys can be accessed via dot-notation; no crufty templatetags necessary:

<!-- Sizers -->
<img src="{{ instance.image.thumbnail.400x400 }}" />
<img src="{{ instance.image.crop.400x400 }}" />

<!-- Filters -->
<img src="{{ instance.image.filters.invert.url }}" />

<!-- Filters + Sizers -->
<img src="{{ instance.image.filters.invert.thumbnail.400x400 }}" />
<img src="{{ instance.image.filters.invert.crop.400x400 }}" />

Note

Using the url attribute on Sizers is optional in templates. Why? All Sizers return an instance of versatileimagefield.datastructures.sizedimage.SizedImageInstance which provides the sized image’s URL via the __unicode__() method (which django’s templating engine looks for when asked to render class instances directly).