mirror of
https://github.com/Hopiu/django-imagekit.git
synced 2026-05-16 00:33:09 +00:00
Some docs
This commit is contained in:
parent
f94b7276b3
commit
58e1c7f7e0
2 changed files with 233 additions and 82 deletions
269
README.rst
269
README.rst
|
|
@ -36,10 +36,87 @@ Specs
|
||||||
-----
|
-----
|
||||||
|
|
||||||
You have one image and you want to do something to it to create another image.
|
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?
|
But how do you tell ImageKit what to do? By defining an image spec.
|
||||||
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
|
An **image spec** is a type of **image generator** that generates a new image
|
||||||
way is by defining an ``ImageSpec`` subclass:
|
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
|
.. code-block:: python
|
||||||
|
|
||||||
|
|
@ -51,51 +128,109 @@ way is by defining an ``ImageSpec`` subclass:
|
||||||
format = 'JPEG'
|
format = 'JPEG'
|
||||||
options = {'quality': 60}
|
options = {'quality': 60}
|
||||||
|
|
||||||
Now that you've defined a spec, it's time to use it. The nice thing about specs
|
It's probaby not surprising that this class is capable of processing an image
|
||||||
is that they can be used in many different contexts.
|
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
|
||||||
Sometimes, you may want to just use a spec to generate a new image file. This
|
on, or what should be done with the result; that's up to you:
|
||||||
might be useful, for example, in view code, or in scripts:
|
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
spec = Thumbnail()
|
source_file = open('/path/to/myimage.jpg')
|
||||||
new_file = spec.apply(source_file)
|
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
|
.. code-block:: python
|
||||||
|
|
||||||
from imagekit import specs
|
dest = open('/path/to/dest.jpg', 'w')
|
||||||
specs.register(Thumbnail, 'myapp:fancy_thumbnail')
|
dest.write(result.read())
|
||||||
|
dest.close()
|
||||||
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.
|
|
||||||
|
|
||||||
|
|
||||||
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
|
.. code-block:: html
|
||||||
|
|
||||||
{% load imagekit %}
|
{% 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
|
.. 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
|
Not generating HTML image tags? No problem. The tag also functions as an
|
||||||
assignment tag, providing access to the underlying file object:
|
assignment tag, providing access to the underlying file object:
|
||||||
|
|
@ -104,72 +239,42 @@ assignment tag, providing access to the underlying file object:
|
||||||
|
|
||||||
{% load imagekit %}
|
{% 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>
|
<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
|
Because it's such a common use case, ImageKit also provides a "thumbnail"
|
||||||
of applying a spec to another one of your model's fields:
|
template tag.
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: html
|
||||||
|
|
||||||
from django.db import models
|
{% load imagekit %}
|
||||||
from imagekit.models import ImageSpecField
|
|
||||||
|
|
||||||
class Photo(models.Model):
|
{% thumbnail '100x50' source_image %}
|
||||||
avatar = models.ImageField(upload_to='avatars')
|
|
||||||
avatar_thumbnail = ImageSpecField(id='myapp:fancy_thumbnail', source='avatar')
|
|
||||||
|
|
||||||
photo = Photo.objects.all()[0]
|
.. note::
|
||||||
print photo.avatar_thumbnail.url # > /media/CACHE/ik/982d5af84cddddfd0fbf70892b4431e4.jpg
|
|
||||||
print photo.avatar_thumbnail.width # > 100
|
|
||||||
|
|
||||||
Since defining a spec, registering it, and using it in a single model field is
|
Comparing this syntax to the generateimage tag above, you'll notice a few
|
||||||
such a common usage, ImakeKit provides a shortcut that allow you to skip
|
differences.
|
||||||
writing a subclass of ``ImageSpec``:
|
|
||||||
|
|
||||||
.. 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
|
Second, we're passing two positional arguments (the dimensions and the
|
||||||
from imagekit.models import ImageSpecField
|
source image) as opposed to the keyword arguments we used with the
|
||||||
from imagekit.processors import ResizeToFill
|
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
|
In Forms
|
||||||
|
|
|
||||||
|
|
@ -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
|
from django.conf import settings
|
||||||
import os
|
import os
|
||||||
from ..utils import format_to_extension, suggest_extension
|
from ..utils import format_to_extension, suggest_extension
|
||||||
|
|
||||||
|
|
||||||
def source_name_as_path(generator):
|
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)
|
source_filename = getattr(generator.source, 'name', None)
|
||||||
|
|
||||||
if source_filename is None or os.path.isabs(source_filename):
|
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):
|
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)
|
source_filename = getattr(generator.source, 'name', None)
|
||||||
|
|
||||||
if source_filename is None or os.path.isabs(source_filename):
|
if source_filename is None or os.path.isabs(source_filename):
|
||||||
|
|
@ -39,6 +72,19 @@ def source_name_dot_hash(generator):
|
||||||
|
|
||||||
|
|
||||||
def 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)
|
format = getattr(generator, 'format', None)
|
||||||
ext = format_to_extension(format) if format else ''
|
ext = format_to_extension(format) if format else ''
|
||||||
return os.path.normpath(os.path.join(settings.IMAGEKIT_GENERATEDFILE_DIR,
|
return os.path.normpath(os.path.join(settings.IMAGEKIT_GENERATEDFILE_DIR,
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue