mirror of
https://github.com/Hopiu/django-imagekit.git
synced 2026-03-16 21:30:23 +00:00
Removed image field from IKModel and fixed a ton of bugs
This commit is contained in:
parent
64f4264c3d
commit
fe5a3cccf3
5 changed files with 84 additions and 51 deletions
|
|
@ -16,7 +16,7 @@ class IKModelBase(ModelBase):
|
|||
if not parents:
|
||||
return
|
||||
|
||||
user_opts = getattr(cls, 'IK', None)
|
||||
user_opts = getattr(cls, 'IKConfig', None)
|
||||
opts = Options(user_opts)
|
||||
|
||||
try:
|
||||
|
|
@ -24,11 +24,13 @@ class IKModelBase(ModelBase):
|
|||
except ImportError:
|
||||
raise ImportError('Unable to load imagekit config module: %s' % opts.config_module)
|
||||
|
||||
for spec in [spec for spec in module.__dict__.values() if \
|
||||
isinstance(spec, type) and issubclass(spec, specs.ImageSpec)]:
|
||||
for spec in [spec for spec in module.__dict__.values() \
|
||||
if isinstance(spec, type) \
|
||||
and issubclass(spec, specs.ImageSpec) \
|
||||
and spec != specs.ImageSpec]:
|
||||
setattr(cls, spec.name(), specs.Descriptor(spec))
|
||||
opts.specs.append(spec)
|
||||
|
||||
|
||||
setattr(cls, '_ik', opts)
|
||||
|
||||
|
||||
|
|
@ -41,12 +43,10 @@ class IKModel(models.Model):
|
|||
"""
|
||||
__metaclass__ = IKModelBase
|
||||
|
||||
image = models.ImageField(_('image'), upload_to='photos')
|
||||
|
||||
class Meta:
|
||||
abstract = True
|
||||
|
||||
class IK:
|
||||
class IKConfig:
|
||||
pass
|
||||
|
||||
def admin_thumbnail_view(self):
|
||||
|
|
@ -59,21 +59,25 @@ class IKModel(models.Model):
|
|||
(self.get_absolute_url(), prop.url)
|
||||
else:
|
||||
return u'<a href="%s"><img src="%s"></a>' % \
|
||||
(self.image.url, prop.url)
|
||||
(self.ik_image_field.url, prop.url)
|
||||
admin_thumbnail_view.short_description = _('Thumbnail')
|
||||
admin_thumbnail_view.allow_tags = True
|
||||
|
||||
@property
|
||||
def ik_image_field(self):
|
||||
return getattr(self, self._ik.image_field)
|
||||
|
||||
@property
|
||||
def cache_dir(self):
|
||||
""" Returns the path to the image cache directory """
|
||||
return os.path.join(os.path.dirname(self.image.path),
|
||||
self._ik.cache_dir_name)
|
||||
return os.path.join(os.path.dirname(self.ik_image_field.path),
|
||||
self._ik.cache_dir)
|
||||
|
||||
@property
|
||||
def cache_url(self):
|
||||
""" Returns a url pointing to the image cache directory """
|
||||
return '/'.join([os.path.dirname(self.image.url),
|
||||
self._ik.cache_dir_name])
|
||||
return '/'.join([os.path.dirname(self.ik_image_field.url),
|
||||
self._ik.cache_dir])
|
||||
|
||||
def _cleanup_cache_dirs(self):
|
||||
try:
|
||||
|
|
@ -94,12 +98,12 @@ class IKModel(models.Model):
|
|||
prop.create()
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
if self._get_pk_val():
|
||||
self._clear_cache()
|
||||
#if self._get_pk_val():
|
||||
# self._clear_cache()
|
||||
super(IKModel, self).save(*args, **kwargs)
|
||||
self._pre_cache()
|
||||
#self._pre_cache()
|
||||
|
||||
def delete(self):
|
||||
assert self._get_pk_val() is not None, "%s object can't be deleted because its %s attribute is set to None." % (self._meta.object_name, self._meta.pk.attname)
|
||||
#self._clear_cache()
|
||||
self._clear_cache()
|
||||
super(IKModel, self).delete()
|
||||
|
|
|
|||
|
|
@ -4,19 +4,14 @@ class Options(object):
|
|||
""" Class handling per-model imagekit options
|
||||
|
||||
"""
|
||||
# Images will be resized to fit if they are larger than max_image_size
|
||||
image_field = 'image'
|
||||
max_image_size = None
|
||||
# Media subdirectories
|
||||
image_dir_name = 'images'
|
||||
cache_dir_name = 'cache'
|
||||
# If given the image view count will be saved as this field name
|
||||
cache_dir = 'ik_cache'
|
||||
save_count_as = None
|
||||
# String pattern used to generate cache file names
|
||||
cache_filename_format = "%(filename)s_%(specname)s.%(extension)s"
|
||||
# Configuration options coded in the models itself
|
||||
config_module = 'imagekit.config'
|
||||
specs = []
|
||||
|
||||
def __init__(self, opts):
|
||||
for key, value in opts.__dict__.iteritems():
|
||||
setattr(self, key, value)
|
||||
setattr(self, key, value)
|
||||
self.specs = []
|
||||
|
|
@ -42,11 +42,11 @@ class Resize(ImageProcessor):
|
|||
box = (box_left, box_upper, box_right, box_lower)
|
||||
image = image.resize((int(resize_x), int(resize_y)), Image.ANTIALIAS).crop(box)
|
||||
else:
|
||||
if not cls.width == 0 and not cls.height == 0:
|
||||
if not cls.width is None and not cls.height is None:
|
||||
ratio = min(float(cls.width)/cur_width,
|
||||
float(cls.height)/cur_height)
|
||||
else:
|
||||
if cls.width == 0:
|
||||
if cls.width is None:
|
||||
ratio = float(cls.height)/cur_height
|
||||
else:
|
||||
ratio = float(cls.width)/cur_width
|
||||
|
|
|
|||
|
|
@ -52,16 +52,20 @@ class Accessor(object):
|
|||
def create(self):
|
||||
if not os.path.isdir(self._obj.cache_dir):
|
||||
os.makedirs(self._obj.cache_dir)
|
||||
self._img = self.spec.process(Image.open(self._obj.image.path), save_as=self.path)
|
||||
self._img = self.spec.process(Image.open(self._obj.ik_image_field.path), save_as=self.path)
|
||||
|
||||
def delete(self):
|
||||
if self.exists:
|
||||
os.remove(self.path)
|
||||
self._img = None
|
||||
|
||||
@property
|
||||
def exists(self):
|
||||
return os.path.isfile(self.path)
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
filename, ext = os.path.splitext(os.path.basename(self._obj.image.path))
|
||||
filename, ext = os.path.splitext(os.path.basename(self._obj.ik_image_field.path))
|
||||
return self._obj._ik.cache_filename_format % \
|
||||
{'filename': filename,
|
||||
'specname': self.spec.name(),
|
||||
|
|
@ -75,17 +79,13 @@ class Accessor(object):
|
|||
def url(self):
|
||||
if not self.exists:
|
||||
self.create()
|
||||
if self.spec.increment_count():
|
||||
if self.spec.increment_count:
|
||||
fieldname = self._obj._ik.save_count_as
|
||||
if fieldname is not None:
|
||||
current_count = getattr(self._obj, fieldname)
|
||||
setattr(self._obj, fieldname, current_count + 1)
|
||||
return '/'.join([self._obj.cache_url, self.name])
|
||||
|
||||
@property
|
||||
def exists(self):
|
||||
return os.path.isfile(self.path)
|
||||
|
||||
@property
|
||||
def image(self):
|
||||
if self._img is None:
|
||||
|
|
@ -113,9 +113,6 @@ class Accessor(object):
|
|||
class Descriptor(object):
|
||||
def __init__(self, spec):
|
||||
self._spec = spec
|
||||
self._prop = None
|
||||
|
||||
def __get__(self, obj, type=None):
|
||||
if self._prop is None:
|
||||
self._prop = Accessor(obj, self._spec)
|
||||
return self._prop
|
||||
return Accessor(obj, self._spec)
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import os
|
||||
import StringIO
|
||||
import tempfile
|
||||
import unittest
|
||||
from django.conf import settings
|
||||
from django.core.files.base import ContentFile
|
||||
|
|
@ -8,34 +8,71 @@ from django.test import TestCase
|
|||
|
||||
from models import IKModel
|
||||
from specs import ImageSpec
|
||||
from imagekit import processors
|
||||
|
||||
from imagekit import Image
|
||||
|
||||
|
||||
class ResizeToWidth(processors.Resize):
|
||||
width = 100
|
||||
|
||||
class ResizeToHeigh(processors.Resize):
|
||||
height = 100
|
||||
|
||||
class ResizeToFit(processors.Resize):
|
||||
width = 100
|
||||
height = 100
|
||||
|
||||
class ResizeCrop(ResizeToFit):
|
||||
crop = True
|
||||
|
||||
class TestResizeToWidth(ImageSpec):
|
||||
access_as = 'to_width'
|
||||
processors = [ResizeToWidth]
|
||||
|
||||
|
||||
IMG_PATH = os.path.join(os.path.dirname(__file__), 'test.jpg')
|
||||
|
||||
|
||||
class TestPhoto(IKModel):
|
||||
""" Minimal ImageModel class for testing """
|
||||
name = models.CharField(max_length=30)
|
||||
image = models.ImageField(upload_to='images')
|
||||
|
||||
class IKConfig:
|
||||
config_module = 'imagekit.tests'
|
||||
|
||||
|
||||
|
||||
class PLTest(TestCase):
|
||||
class IKTest(TestCase):
|
||||
""" Base TestCase class """
|
||||
def setUp(self):
|
||||
Image.new('RGB', (100, 100)).save(IMG_PATH, 'JPEG')
|
||||
self.p = TestPhoto(name='landscape')
|
||||
self.p.image.save(os.path.basename(IMG_PATH),
|
||||
ContentFile(open(IMG_PATH, 'rb').read()))
|
||||
# create a test image using tempfile and PIL
|
||||
self.tmp = tempfile.TemporaryFile()
|
||||
Image.new('RGB', (800, 600)).save(self.tmp, 'JPEG')
|
||||
self.tmp.seek(0)
|
||||
self.p = TestPhoto()
|
||||
self.p.image.save(os.path.basename('test.jpg'),
|
||||
ContentFile(self.tmp.read()))
|
||||
self.p.save()
|
||||
# destroy temp file
|
||||
self.tmp.close()
|
||||
|
||||
def test_config(self):
|
||||
self.assertEqual(self.p._ik.specs, [TestResizeToWidth])
|
||||
|
||||
def test_setup(self):
|
||||
self.assert_(self.p.image is not None)
|
||||
self.assertEqual(self.p.image.width, 100 )
|
||||
self.assertEqual(self.p.image.width, 800)
|
||||
self.assertEqual(self.p.image.height, 600)
|
||||
|
||||
def test_accessor(self):
|
||||
self.assertEqual(self.p.admin_thumbnail.width, 100)
|
||||
def test_to_width(self):
|
||||
self.assertEqual(self.p.to_width.width, 100)
|
||||
self.assertEqual(self.p.to_width.height, 75)
|
||||
|
||||
def test_url(self):
|
||||
url = "%s/%s" % (self.p.cache_url, 'test_to_width.jpg')
|
||||
self.assertEqual(self.p.to_width.url, url)
|
||||
|
||||
def tearDown(self):
|
||||
os.remove(IMG_PATH)
|
||||
# make sure image file is deleted
|
||||
path = self.p.image.path
|
||||
self.p.delete()
|
||||
self.failIf(os.path.isfile(path))
|
||||
|
|
|
|||
Loading…
Reference in a new issue