Some docs

This commit is contained in:
Matthew Tretter 2013-02-01 01:27:54 -05:00
parent f94b7276b3
commit 58e1c7f7e0
2 changed files with 233 additions and 82 deletions

View file

@ -36,10 +36,87 @@ Specs
-----
You have one image and you want to do something to it to create another image.
That's the basic use case of ImageKit. But how do you tell ImageKit what to do?
By defining an "image spec." Specs are instructions for creating a new image
from an existing one, and there are a few ways to define one. The most basic
way is by defining an ``ImageSpec`` subclass:
But how do you tell ImageKit what to do? By defining an image spec.
An **image spec** is a type of **image generator** that generates a new image
from a source image.
Defining Specs In Models
^^^^^^^^^^^^^^^^^^^^^^^^
The easiest way to use define an image spec is by using an ImageSpecField on
your model class:
.. code-block:: python
from django.db import models
from imagekit.models import ImageSpecField
from imagekit.processors import ResizeToFill
class Profile(models.Model):
avatar = models.ImageField(upload_to='avatars')
avatar_thumbnail = ImageSpecField(source='avatar',
processors=[ResizeToFill(100, 50)],
format='JPEG',
options={'quality': 60})
profile = Profile.objects.all()[0]
print profile.avatar_thumbnail.url # > /media/generated/images/982d5af84cddddfd0fbf70892b4431e4.jpg
print profile.avatar_thumbnail.width # > 100
As you can probably tell, ImageSpecFields work a lot like Django's
ImageFields. The difference is that they're automatically generated by
ImageKit based on the instructions you give. In the example above, the avatar
thumbnail is a resized version of the avatar image, saved as a JPEG with a
quality of 60.
Sometimes, however, you don't need to keep the original image (the avatar in
the above example); when the user uploads an image, you just want to process it
and save the result. In those cases, you can use the ``ProcessedImageField``
class:
.. code-block:: python
from django.db import models
from imagekit.models import ProcessedImageField
class Profile(models.Model):
avatar_thumbnail = ProcessedImageField(upload_to='avatars',
processors=[ResizeToFill(100, 50)],
format='JPEG',
options={'quality': 60})
profile = Profile.objects.all()[0]
print profile.avatar_thumbnail.url # > /media/avatars/MY-avatar.jpg
print profile.avatar_thumbnail.width # > 100
This is pretty similar to our previous example. We don't need to specify a
"source" any more since we're not processing another image field, but we do need
to pass an "upload_to" argument. This behaves exactly as it does for Django
``ImageField``s.
.. note::
You might be wondering why we didn't need an "upload_to" argument for our
ImageSpecField. The reason is that ProcessedImageFields really are just like
ImageFields—they save the file path in the database and you need to run
syncdb (or create a migration) when you add one to your model.
ImageSpecFields, on the other hand, are virtual—they add no fields to your
database and don't require a database. This is handy for a lot of reasons,
but it means that the path to the image file needs to be programmatically
constructed based on the source image and the spec.
Defining Specs Outside of Models
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Defining specs as models fields is one very convenient way to process images,
but it isn't the only way. Sometimes you can't (or don't want to) add fields to
your models, and that's okay. You can define image spec classes and use them
directly. This can be especially useful for doing image processing in views—
particularly when the processing being done depends on user input.
.. code-block:: python
@ -51,51 +128,109 @@ way is by defining an ``ImageSpec`` subclass:
format = 'JPEG'
options = {'quality': 60}
Now that you've defined a spec, it's time to use it. The nice thing about specs
is that they can be used in many different contexts.
Sometimes, you may want to just use a spec to generate a new image file. This
might be useful, for example, in view code, or in scripts:
It's probaby not surprising that this class is capable of processing an image
in the exact same way as our ImageSpecField above. However, unlike with the
image spec model field, this class doesn't define what source the spec is acting
on, or what should be done with the result; that's up to you:
.. code-block:: python
spec = Thumbnail()
new_file = spec.apply(source_file)
source_file = open('/path/to/myimage.jpg')
image_generator = Thumbnail(source=source_file)
result = image_generator.generate()
More often, however, you'll want to register your spec with ImageKit:
The result of calling ``generate()`` on an image spec is a file-like object
containing our resized image, with which you can do whatever you want. For
example, if you wanted to save it to disk:
.. code-block:: python
from imagekit import specs
specs.register(Thumbnail, 'myapp:fancy_thumbnail')
Once a spec is registered with a unique name, you can start to take advantage of
ImageKit's powerful utilities to automatically generate images for you...
.. note:: You might be wondering why we bother with the id string instead of
just passing the spec itself. The reason is that these ids allow users to
easily override specs defined in third party apps. That way, it doesn't
matter if "django-badblog" says its thumbnails are 200x200, you can just
register your own spec (using the same id the app uses) and have whatever
size thumbnails you want.
dest = open('/path/to/dest.jpg', 'w')
dest.write(result.read())
dest.close()
In Templates
^^^^^^^^^^^^
Using Specs In Templates
^^^^^^^^^^^^^^^^^^^^^^^^
One utility ImageKit provides for processing images is a template tag:
If you have a model with an ImageSpecField or ProcessedImageField, you can
easily use those processed image just as you would a normal image field:
.. code-block:: html
<img src="{{ profile.avatar_thumbnail.url }}" />
(This is assuming you have a view that's setting a context variable named
"profile" to an instance of our Profile model.)
But you can also generate processed image files directly in your template—from
any image—without adding anything to your model. In order to do this, you'll
first have to define an image generator class (remember, specs are a type of
generator) in your app somewhere, just as we did in the last section. You'll
also need a way of referring to the generator in your template, so you'll need
to register it.
.. code-block:: python
from imagekit import ImageSpec
from imagekit.processors import ResizeToFill
class Thumbnail(ImageSpec):
processors = [ResizeToFill(100, 50)]
format = 'JPEG'
options = {'quality': 60}
register.generator('myapp:thumbnail', Thumbnail)
.. note::
You can register your generator with any id you want, but choose wisely!
If you pick something too generic, you could have a conflict with another
third-party app you're using. For this reason, it's a good idea to prefix
your generator ids with the name of your app. Also, ImageKit recognizes
colons as separators when doing pattern matching (e.g. in the generateimages
management command), so it's a good idea to use those too!
.. warning::
This code can go in any file you want—but you need to make sure it's loaded!
In order to keep things simple, ImageKit will automatically try to load an
module named "imagegenerators" in each of your installed apps. So why don't
you just save yourself the headache and put your image specs in there?
Now that we've created an image generator class and registered it with ImageKit,
we can use it in our templates!
generateimage
"""""""""""""
The most generic template tag that ImageKit gives you is called "generateimage".
It requires at least one argument: the id of a registered image generator.
Additional keyword-style arguments are passed to the registered generator class.
As we saw above, image spec constructors expect a source keyword argument, so
that's what we need to pass to use our thumbnail spec:
.. code-block:: html
{% load imagekit %}
{% spec 'myapp:fancy_thumbnail' source_image alt='A picture of me' %}
{% generateimage 'myapp:thumbnail' source=source_image %}
Output:
This will output the following HTML:
.. code-block:: html
<img src="/media/CACHE/ik/982d5af84cddddfd0fbf70892b4431e4.jpg" width="100" height="50" alt="A picture of me" />
<img src="/media/generated/iimages/982d5af84cddddfd0fbf70892b4431e4.jpg" width="100" height="50" />
You can also add additional HTML attributes; just separate them from your
keyword args using two dashes:
.. code-block:: html
{% load imagekit %}
{% generateimage 'myapp:thumbnail' source=source_image -- alt="A picture of Me" id="mypicture" %}
Not generating HTML image tags? No problem. The tag also functions as an
assignment tag, providing access to the underlying file object:
@ -104,72 +239,42 @@ assignment tag, providing access to the underlying file object:
{% load imagekit %}
{% spec 'myapp:fancy_thumbnail' source_image as th %}
{% generateimage 'myapp:thumbnail' source=source_image as th %}
<a href="{{ th.url }}">Click to download a cool {{ th.width }} x {{ th.height }} image!</a>
In Models
^^^^^^^^^
thumbnail
"""""""""
Specs can also be used to add ``ImageField``-like fields that expose the result
of applying a spec to another one of your model's fields:
Because it's such a common use case, ImageKit also provides a "thumbnail"
template tag.
.. code-block:: python
.. code-block:: html
from django.db import models
from imagekit.models import ImageSpecField
{% load imagekit %}
class Photo(models.Model):
avatar = models.ImageField(upload_to='avatars')
avatar_thumbnail = ImageSpecField(id='myapp:fancy_thumbnail', source='avatar')
{% thumbnail '100x50' source_image %}
photo = Photo.objects.all()[0]
print photo.avatar_thumbnail.url # > /media/CACHE/ik/982d5af84cddddfd0fbf70892b4431e4.jpg
print photo.avatar_thumbnail.width # > 100
.. note::
Since defining a spec, registering it, and using it in a single model field is
such a common usage, ImakeKit provides a shortcut that allow you to skip
writing a subclass of ``ImageSpec``:
Comparing this syntax to the generateimage tag above, you'll notice a few
differences.
.. code-block:: python
First, we didn't have to specify an image generator id; unless we tell it
otherwise, thumbnail tag uses the generator registered with the id
"imagekit:thumbnail". (A custom id can be specified by passing an argument
before the dimensions.) **It's important to note that this tag is *not*
using the Thumbnail spec class we defined earlier**; it's using the
generator registered with the id "imagekit:thumbnail" which, by default, is
``imagekit.generatorlibrary.Thumbnail``.
from django.db import models
from imagekit.models import ImageSpecField
from imagekit.processors import ResizeToFill
Second, we're passing two positional arguments (the dimensions and the
source image) as opposed to the keyword arguments we used with the
generateimage tag. Interally, however, the tag is parsing our positional
arguments and passing them as keyword arguments to our generator class.
class Photo(models.Model):
avatar = models.ImageField(upload_to='avatars')
avatar_thumbnail = ImageSpecField(processors=[ResizeToFill(100, 50)],
format='JPEG',
options={'quality': 60},
source='avatar')
photo = Photo.objects.all()[0]
print photo.avatar_thumbnail.url # > /media/CACHE/ik/982d5af84cddddfd0fbf70892b4431e4.jpg
print photo.avatar_thumbnail.width # > 100
This has the exact same behavior as before, but the spec definition is inlined.
Since no ``id`` is provided, one is automatically generated based on the app
name, model, and field.
Specs can also be used in models to add ``ImageField``-like fields that process
a user-provided image without saving the original:
.. code-block:: python
from django.db import models
from imagekit.models import ProcessedImageField
class Photo(models.Model):
avatar_thumbnail = ProcessedImageField(spec_id='myapp:fancy_thumbnail',
upload_to='avatars')
photo = Photo.objects.all()[0]
print photo.avatar_thumbnail.url # > /media/avatars/MY-avatar_3.jpg
print photo.avatar_thumbnail.width # > 100
Like with ``ImageSpecField``, the ``ProcessedImageField`` constructor also
has a shortcut version that allows you to inline spec definitions.
In Forms

View file

@ -1,9 +1,29 @@
"""
Functions responsible for returning filenames for the given image generator.
Users are free to define their own functions; these are just some some sensible
choices.
"""
from django.conf import settings
import os
from ..utils import format_to_extension, suggest_extension
def source_name_as_path(generator):
"""
A namer that, given the following source file name::
photos/thumbnails/bulldog.jpg
will generate a name like this::
/path/to/generated/images/photos/thumbnails/bulldog/5ff3233527c5ac3e4b596343b440ff67.jpg
where "/path/to/generated/images/" is the value specified by the
``IMAGEKIT_GENERATEDFILE_DIR`` setting.
"""
source_filename = getattr(generator.source, 'name', None)
if source_filename is None or os.path.isabs(source_filename):
@ -21,6 +41,19 @@ def source_name_as_path(generator):
def source_name_dot_hash(generator):
"""
A namer that, given the following source file name::
photos/thumbnails/bulldog.jpg
will generate a name like this::
/path/to/generated/images/photos/thumbnails/bulldog.5ff3233527c5.jpg
where "/path/to/generated/images/" is the value specified by the
``IMAGEKIT_GENERATEDFILE_DIR`` setting.
"""
source_filename = getattr(generator.source, 'name', None)
if source_filename is None or os.path.isabs(source_filename):
@ -39,6 +72,19 @@ def source_name_dot_hash(generator):
def hash(generator):
"""
A namer that, given the following source file name::
photos/thumbnails/bulldog.jpg
will generate a name like this::
/path/to/generated/images/5ff3233527c5ac3e4b596343b440ff67.jpg
where "/path/to/generated/images/" is the value specified by the
``IMAGEKIT_GENERATEDFILE_DIR`` setting.
"""
format = getattr(generator, 'format', None)
ext = format_to_extension(format) if format else ''
return os.path.normpath(os.path.join(settings.IMAGEKIT_GENERATEDFILE_DIR,