diff --git a/imagekit/imagecache/__init__.py b/imagekit/imagecache/__init__.py new file mode 100644 index 0000000..3c582c8 --- /dev/null +++ b/imagekit/imagecache/__init__.py @@ -0,0 +1,33 @@ +from django.core.exceptions import ImproperlyConfigured +from django.utils.importlib import import_module + +from imagekit.imagecache.base import InvalidImageCacheBackendError, PessimisticImageCacheBackend, NonValidatingImageCacheBackend + +_default_image_cache_backend = None + + +def get_default_image_cache_backend(): + """ + Get the default image cache backend. Uses the same method as + django.core.file.storage.get_storage_class + + """ + global _default_image_cache_backend + if not _default_image_cache_backend: + from ..settings import DEFAULT_IMAGE_CACHE_BACKEND as import_path + try: + dot = import_path.rindex('.') + except ValueError: + raise ImproperlyConfigured("%s isn't an image cache backend module." % \ + import_path) + module, classname = import_path[:dot], import_path[dot + 1:] + try: + mod = import_module(module) + except ImportError, e: + raise ImproperlyConfigured('Error importing image cache backend module %s: "%s"' % (module, e)) + try: + cls = getattr(mod, classname) + _default_image_cache_backend = cls() + except AttributeError: + raise ImproperlyConfigured('Image cache backend module "%s" does not define a "%s" class.' % (module, classname)) + return _default_image_cache_backend diff --git a/imagekit/imagecache.py b/imagekit/imagecache/base.py similarity index 55% rename from imagekit/imagecache.py rename to imagekit/imagecache/base.py index 0edc8d6..f06c9b5 100644 --- a/imagekit/imagecache.py +++ b/imagekit/imagecache/base.py @@ -1,5 +1,8 @@ from django.core.exceptions import ImproperlyConfigured -from django.utils.importlib import import_module + + +class InvalidImageCacheBackendError(ImproperlyConfigured): + pass class PessimisticImageCacheBackend(object): @@ -55,33 +58,3 @@ class NonValidatingImageCacheBackend(object): def clear(self, file): file.delete(save=False) - - -_default_image_cache_backend = None - - -def get_default_image_cache_backend(): - """ - Get the default image cache backend. Uses the same method as - django.core.file.storage.get_storage_class - - """ - global _default_image_cache_backend - if not _default_image_cache_backend: - from .settings import DEFAULT_IMAGE_CACHE_BACKEND as import_path - try: - dot = import_path.rindex('.') - except ValueError: - raise ImproperlyConfigured("%s isn't an image cache backend module." % \ - import_path) - module, classname = import_path[:dot], import_path[dot + 1:] - try: - mod = import_module(module) - except ImportError, e: - raise ImproperlyConfigured('Error importing image cache backend module %s: "%s"' % (module, e)) - try: - cls = getattr(mod, classname) - _default_image_cache_backend = cls() - except AttributeError: - raise ImproperlyConfigured('Image cache backend module "%s" does not define a "%s" class.' % (module, classname)) - return _default_image_cache_backend diff --git a/imagekit/imagecache/celery.py b/imagekit/imagecache/celery.py new file mode 100644 index 0000000..7e3cf46 --- /dev/null +++ b/imagekit/imagecache/celery.py @@ -0,0 +1,41 @@ +# -*- coding: utf-8 -*- +from __future__ import absolute_import + +from imagekit.imagecache import PessimisticImageCacheBackend, InvalidImageCacheBackendError + + +def generate(model, pk, attr): + try: + instance = model._default_manager.get(pk=pk) + except model.DoesNotExist: + pass # The model was deleted since the task was scheduled. NEVER MIND! + else: + getattr(instance, attr).generate(save=True) + + +class CeleryImageCacheBackend(PessimisticImageCacheBackend): + """ + A pessimistic cache state backend that uses celery to generate its spec + images. Like PessimisticCacheStateBackend, this one checks to see if the + file exists on validation, so the storage is hit fairly frequently, but an + image is guaranteed to exist. However, while validation guarantees the + existence of *an* image, it does not necessarily guarantee that you will get + the correct image, as the spec may be pending regeneration. In other words, + while there are `generate` tasks in the queue, it is possible to get a + stale spec image. The tradeoff is that calling `invalidate()` won't block + to interact with file storage. + + """ + def __init__(self): + try: + from celery.task import task + except: + raise InvalidImageCacheBackendError("Celery image cache backend requires either the 'celery' library") + if not getattr(CeleryImageCacheBackend, '_task', None): + CeleryImageCacheBackend._task = task(generate) + + def invalidate(self, file): + self._task.delay(file.instance.__class__, file.instance.pk, file.attname) + + def clear(self, file): + file.delete(save=False)