Implemented post_save and post_delete handlers for ImageSpecs.

Removed the save and clear_cache methods from ImageModel (along with helpers).
Now, whenever an ImageSpec is contributed to a model, handlers are created for
the post_save and post_delete signals. The post_save handler does the work of
running the ImageSpec processors and caching the resulting file, while the
post_delete handler does the work cleaning up the cached files.
This commit is contained in:
Eric Eldredge 2011-09-20 20:19:19 -04:00
parent 34e475885b
commit b1c5432310
2 changed files with 49 additions and 62 deletions

View file

@ -72,68 +72,6 @@ class ImageModel(models.Model):
class IKOptions:
pass
def _clear_cache(self):
for spec in self._ik.specs:
prop = getattr(self, spec.name())
prop._delete()
def _pre_cache(self):
for spec in self._ik.specs:
if spec.pre_cache:
prop = getattr(self, spec.name())
prop._create()
def save_image(self, name, image, save=True, replace=True):
imgfields = self._imgfields
for imgfield in imgfields:
if imgfield and replace:
imgfield.delete(save=False)
if hasattr(image, 'read'):
data = image.read()
else:
data = image
content = ContentFile(data)
imgfield.save(name, content, save)
@property
def _imgfields(self):
return set([spec._get_imgfield(self) for spec in self._ik.specs])
def save(self, clear_cache=True, *args, **kwargs):
super(ImageModel, self).save(*args, **kwargs)
is_new_object = self._get_pk_val() is None
if is_new_object:
clear_cache = False
imgfields = self._imgfields
for imgfield in imgfields:
spec = self._ik.preprocessor_spec
if spec is not None:
newfile = imgfield.storage.open(str(imgfield))
img = Image.open(newfile)
img, format = spec.process(img, self)
if format != 'JPEG':
imgfile = img_to_fobj(img, format)
else:
imgfile = img_to_fobj(img, format,
quality=int(spec.quality),
optimize=True)
content = ContentFile(imgfile.read())
newfile.close()
name = str(imgfield)
imgfield.storage.delete(name)
imgfield.storage.save(name, content)
if self._imgfields:
if clear_cache:
self._clear_cache()
self._pre_cache()
def clear_cache(self, **kwargs):
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()
post_delete.connect(ImageModel.clear_cache, sender=ImageModel)

View file

@ -12,6 +12,7 @@ from imagekit.lib import *
from imagekit.utils import img_to_fobj
from django.core.files.base import ContentFile
from django.utils.encoding import force_unicode, smart_str
from django.db.models.signals import post_save, post_delete
class ImageSpec(object):
@ -63,6 +64,16 @@ class ImageSpec(object):
def contribute_to_class(self, cls, name):
setattr(cls, name, _ImageSpecDescriptor(self, name))
# Connect to the signals only once for this class's attribute.
uid = '%s.%s_%s' % (cls.__module__, cls.__name__, name)
post_save.connect(_create_post_save_handler(name),
sender=cls,
weak=False,
dispatch_uid='%s_save' % uid)
post_delete.connect(_create_post_delete_handler(name, uid),
sender=cls,
weak=False,
dispatch_uid='%s.delete' % uid)
class BoundImageSpec(ImageSpec):
@ -227,3 +238,41 @@ class _ImageSpecDescriptor(object):
return self._spec
else:
return BoundImageSpec(instance, self._spec, self._property_name)
def _create_post_save_handler(accessor_name):
def handler(sender, instance=None, created=False, raw=False, **kwargs):
if raw:
return
accessor = getattr(instance, accessor_name)
spec = accessor.spec
imgfield = spec._get_imgfield(instance)
newfile = imgfield.storage.open(str(imgfield))
img = Image.open(newfile)
img, format = spec.process(img, instance)
if format != 'JPEG':
imgfile = img_to_fobj(img, format)
else:
imgfile = img_to_fobj(img, format,
quality=int(spec.quality),
optimize=True)
content = ContentFile(imgfile.read())
newfile.close()
name = str(imgfield)
imgfield.storage.delete(name)
imgfield.storage.save(name, content)
if not created:
accessor._delete()
accessor._create()
return handler
def _create_post_delete_handler(accessor_name, uid):
def handler(sender, instance=None, **kwargs):
assert instance._get_pk_val() is not None, "%s object can't be deleted because its %s attribute is set to None." % (instance._meta.object_name, instance._meta.pk.attname)
accessor = getattr(instance, accessor_name)
accessor._delete()
post_save.disconnect(dispatch_uid='%s_save' % uid)
post_delete.disconnect(dispatch_uid='%s.delete' % uid)
return handler