django-imagekit/imagekit/models/fields/__init__.py

127 lines
5.1 KiB
Python
Raw Permalink Normal View History

from __future__ import unicode_literals
from django.conf import settings
2008-12-28 21:48:21 +00:00
from django.db import models
from django.db.models.signals import class_prepared
2012-10-13 02:36:13 +00:00
from .files import ProcessedImageFieldFile
from .utils import ImageSpecFileDescriptor
2012-10-13 02:36:13 +00:00
from ...specs import SpecHost
2012-12-01 20:51:28 +00:00
from ...specs.sourcegroups import ImageFieldSourceGroup
from ...registry import register
class SpecHostField(SpecHost):
def _set_spec_id(self, cls, name):
spec_id = getattr(self, 'spec_id', None)
# Generate a spec_id to register the spec with. The default spec id is
# "<app>:<model>_<field>"
2013-04-05 18:57:41 +00:00
if not spec_id:
spec_id = ('%s:%s:%s' % (cls._meta.app_label,
cls._meta.object_name, name)).lower()
2013-04-05 18:57:41 +00:00
# Register the spec with the id. This allows specs to be overridden
# later, from outside of the model definition.
super(SpecHostField, self).set_spec_id(spec_id)
class ImageSpecField(SpecHostField):
2011-10-31 14:12:03 +00:00
"""
The heart and soul of the ImageKit library, ImageSpecField allows you to add
2011-09-26 00:38:43 +00:00
variants of uploaded images to your models.
2009-12-19 16:01:54 +00:00
"""
2012-04-10 01:25:46 +00:00
def __init__(self, processors=None, format=None, options=None,
source=None, cachefile_storage=None, autoconvert=None,
cachefile_backend=None, cachefile_strategy=None, spec=None,
2012-11-06 04:34:32 +00:00
id=None):
2011-09-21 15:37:29 +00:00
2012-10-14 22:48:17 +00:00
SpecHost.__init__(self, processors=processors, format=format,
options=options, cachefile_storage=cachefile_storage,
autoconvert=autoconvert,
cachefile_backend=cachefile_backend,
cachefile_strategy=cachefile_strategy, spec=spec,
spec_id=id)
2012-10-05 01:37:20 +00:00
# TODO: Allow callable for source. See https://github.com/matthewwithanm/django-imagekit/issues/158#issuecomment-10921664
self.source = source
2012-10-05 01:37:20 +00:00
2011-09-21 15:37:29 +00:00
def contribute_to_class(self, cls, name):
# If the source field name isn't defined, figure it out.
def register_source_group(source):
setattr(cls, name, ImageSpecFileDescriptor(self, name, source))
self._set_spec_id(cls, name)
# Add the model and field as a source for this spec id
register.source_group(self.spec_id, ImageFieldSourceGroup(cls, source))
if self.source:
register_source_group(self.source)
else:
# The source argument is not defined
# Then we need to see if there is only one ImageField in that model
# But we need to do that after full model initialization
def handle_model_preparation(sender, **kwargs):
image_fields = [f.attname for f in cls._meta.fields if
isinstance(f, models.ImageField)]
if len(image_fields) == 0:
raise Exception(
'%s does not define any ImageFields, so your %s'
' ImageSpecField has no image to act on.' %
(cls.__name__, name))
elif len(image_fields) > 1:
raise Exception(
'%s defines multiple ImageFields, but you have not'
' specified a source for your %s ImageSpecField.' %
(cls.__name__, name))
register_source_group(image_fields[0])
class_prepared.connect(handle_model_preparation, sender=cls, weak=False)
2011-09-21 15:37:29 +00:00
class ProcessedImageField(models.ImageField, SpecHostField):
2011-10-31 14:12:03 +00:00
"""
ProcessedImageField is an ImageField that runs processors on the uploaded
2011-09-26 00:38:43 +00:00
image *before* saving it to storage. This is in contrast to specs, which
maintain the original. Useful for coercing fileformats or keeping images
within a reasonable size.
"""
attr_class = ProcessedImageFieldFile
2012-04-10 01:25:46 +00:00
def __init__(self, processors=None, format=None, options=None,
2012-11-06 04:34:32 +00:00
verbose_name=None, name=None, width_field=None, height_field=None,
autoconvert=None, spec=None, spec_id=None, **kwargs):
2011-09-26 00:38:43 +00:00
"""
The ProcessedImageField constructor accepts all of the arguments that
2011-10-31 14:12:03 +00:00
the :class:`django.db.models.ImageField` constructor accepts, as well
as the ``processors``, ``format``, and ``options`` arguments of
:class:`imagekit.models.ImageSpecField`.
2011-09-26 00:38:43 +00:00
"""
# if spec is not provided then autoconvert will be True by default
if spec is None and autoconvert is None:
autoconvert = True
SpecHost.__init__(self, processors=processors, format=format,
options=options, autoconvert=autoconvert, spec=spec,
spec_id=spec_id)
models.ImageField.__init__(self, verbose_name, name, width_field,
height_field, **kwargs)
def contribute_to_class(self, cls, name):
self._set_spec_id(cls, name)
return super(ProcessedImageField, self).contribute_to_class(cls, name)
# If the project does not use south, then we will not try to add introspection
if 'south' in settings.INSTALLED_APPS:
try:
from south.modelsinspector import add_introspection_rules
except ImportError:
pass
else:
add_introspection_rules([], [r'^imagekit\.models\.fields\.ProcessedImageField$'])