Login

Imagefield with variations

Author:
fivethreeo
Posted:
August 14, 2008
Language:
Python
Version:
.96
Score:
2 (after 2 ratings)

Usage:

from django.db import models
from imagevariations.fields import ImageVariationsField, Thumbnail

class Image(models.Model):
    name = models.CharField(max_length=20)
    image = ImageVariationsField(upload_to='testimages', variations=(Thumbnail,) )

How to use in templates:

Use the lowercase name of the image variation class.

{{ object.image.variations.thumbnail.url }}

By default all image variations will use the same storage backend as the field but can be replaced per variation by setting self.storage on the variation class.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
from django.db.models.fields.files import ImageField, ImageFieldFile
from django.core.files.base import File, ContentFile

class ImageVariation(File):

    variation_suffix = 'variation'

    def __init__(self, instance):
        self.field = instance.field
        self.instance = instance.instance
        self.storage = getattr(self, 'storage', instance.storage)
        name = getattr(self.instance, self.field.name) or u''
        self._name = self.get_variation_name(unicode(name))
        self._closed = False

    def get_variation_name(self, name):
        try:
            dot_index = name.rindex('.')
        except:
            name = '%s%s' % (name, self.variation_suffix)
        else:
            name = '%s%s%s' % (name[:dot_index], self.variation_suffix, name[dot_index:])
        return name

    def save(self, name, content):
        self.storage.save(self.name, content)

    def delete(self):
        self.close()
        self.storage.delete(self.name)
    def _require_file(self):
        if not self:
            raise ValueError("The '%s' attribute has no file associated with it." % self.field.name)

    def _get_file(self):
        self._require_file()
        if not hasattr(self, '_file'):
            self._file = self.storage.open(self.name, 'rb')
        return self._file
    file = property(_get_file)

    def _get_path(self):
        self._require_file()
        return self.storage.path(self.name)
    path = property(_get_path)

    def _get_url(self):
        self._require_file()
        return self.storage.url(self.name)
    url = property(_get_url)

    def open(self, mode='rb'):
        self._require_file()
        return super(ImageVariation, self).open(mode)
    # open() doesn't alter the file's contents, but it does reset the pointer
    open.alters_data = True

from StringIO import StringIO
def get_file(data):
    if hasattr(data, 'temporary_file_path'):
         file = data.temporary_file_path()
    else:
        if hasattr(data, 'read'):
            file = StringIO(data.read())
        else:
            file = StringIO(data['content'])
    return file

WIDTH, HEIGHT = 0, 1
def resize_image(content, size):
    from PIL import Image
    img = Image.open(get_file(content))
    if img.size[0] > size['width'] or img.size[1] > size['height']:
        if size['force']:
            target_height = float(size['height'] * img.size[WIDTH]) / size['width']
            if target_height < img.size[HEIGHT]: # Crop height
                crop_side_size = int((img.size[HEIGHT] - target_height) / 2)
                img = img.crop((0, crop_side_size, img.size[WIDTH], img.size[HEIGHT] - crop_side_size))
            elif target_height > img.size[HEIGHT]: # Crop width
                target_width = float(size['width'] * img.size[HEIGHT]) / size['height']
                crop_side_size = int((img.size[WIDTH] - target_width) / 2)
                img = img.crop((crop_side_size, 0, img.size[WIDTH] - crop_side_size, img.size[HEIGHT]))
        img.thumbnail((size['width'], size['height']), Image.ANTIALIAS)
        out = StringIO()
        try:
            img.save(out, optimize=1)
        except IOError:
            img.save(out)
        return out
    else:
        return content

class Thumbnail(ImageVariation):
    variation_suffix = 'thumbnail'

    def save(self, name, content):
        self.storage.save(self.name, resize_image(content, {'height': 200, 'width': 200, 'force': True} ) )

class ImageVariationsDescriptor(object):

    def __get__(self, instance=None, owner=None):
        self.variations = [ (v(instance))
            for v in instance.field.variations ]
        for v in self.variations:
            setattr(self, v.__class__.__name__.lower(), v)
        return self

    def save(self, name, content):
        for v in self.variations:
            content.seek(0)
            v.save(name, content)

    def delete(self):
        for v in self.variations:
            v.delete()


class ImageVariationsFieldFile(ImageFieldFile):

    variations = ImageVariationsDescriptor()

    def save(self, name, content, save=True):
        super(ImageVariationsFieldFile, self).save(name, content, save=save)
        self.variations.save(name, content)

    def delete(self, save=True):
        self.variations.delete()
        super(ImageVariationsFieldFile, self).delete(save)

class ImageVariationsField(ImageField):
    attr_class = ImageVariationsFieldFile

    def __init__(self, variations=[], *args, **kwargs):
        self.variations = variations
        super(ImageVariationsField, self).__init__(*args, **kwargs)

More like this

  1. Template tag - list punctuation for a list of items by shapiromatron 10 months, 2 weeks ago
  2. JSONRequestMiddleware adds a .json() method to your HttpRequests by cdcarter 10 months, 3 weeks ago
  3. Serializer factory with Django Rest Framework by julio 1 year, 5 months ago
  4. Image compression before saving the new model / work with JPG, PNG by Schleidens 1 year, 6 months ago
  5. Help text hyperlinks by sa2812 1 year, 6 months ago

Comments

Please login first before commenting.