mirror of
https://github.com/Hopiu/django-imagekit.git
synced 2026-03-23 08:20:25 +00:00
Merge branch 'ik-next' into cacheables
Conflicts: imagekit/management/commands/warmimagecache.py imagekit/registry.py
This commit is contained in:
commit
cef3a41d86
9 changed files with 109 additions and 45 deletions
|
|
@ -5,7 +5,7 @@ from django.utils.encoding import smart_str, smart_unicode
|
|||
import os
|
||||
from .signals import before_access
|
||||
from .utils import (format_to_mimetype, extension_to_mimetype, get_logger,
|
||||
get_singleton)
|
||||
get_singleton, generate)
|
||||
|
||||
|
||||
class BaseIKFile(File):
|
||||
|
|
@ -115,7 +115,8 @@ class GeneratedImageCacheFile(BaseIKFile, ImageFile):
|
|||
|
||||
def generate(self):
|
||||
# Generate the file
|
||||
content = self.generator.generate()
|
||||
content = generate(self.generator)
|
||||
|
||||
actual_name = self.storage.save(self.name, content)
|
||||
|
||||
if actual_name != self.name:
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
from django.forms import ImageField
|
||||
from ..specs import SpecHost
|
||||
from ..utils import generate
|
||||
|
||||
|
||||
class ProcessedImageField(ImageField, SpecHost):
|
||||
|
|
@ -22,7 +23,7 @@ class ProcessedImageField(ImageField, SpecHost):
|
|||
data = super(ProcessedImageField, self).clean(data, initial)
|
||||
|
||||
if data:
|
||||
spec = self.get_spec() # HINTS?!?!?!?!?!
|
||||
data = spec.apply(data, data.name)
|
||||
spec = self.get_spec(source=data)
|
||||
data = generate(spec)
|
||||
|
||||
return data
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
from django.db.models.fields.files import ImageFieldFile
|
||||
import os
|
||||
from ...utils import suggest_extension
|
||||
from ...utils import suggest_extension, generate
|
||||
|
||||
|
||||
class ProcessedImageFieldFile(ImageFieldFile):
|
||||
def save(self, name, content, save=True):
|
||||
filename, ext = os.path.splitext(name)
|
||||
spec = self.field.get_spec() # TODO: What "hints"?
|
||||
spec = self.field.get_spec(source=content)
|
||||
ext = suggest_extension(name, spec.format)
|
||||
new_name = '%s%s' % (filename, ext)
|
||||
content = spec.apply(content, new_name)
|
||||
content = generate(spec)
|
||||
return super(ProcessedImageFieldFile, self).save(new_name, content, save)
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ class ImageSpecFileDescriptor(object):
|
|||
self.attname))
|
||||
else:
|
||||
source = image_fields[0]
|
||||
spec = self.field.get_spec(source=source) # TODO: What "hints" should we pass here?
|
||||
spec = self.field.get_spec(source=source)
|
||||
file = GeneratedImageCacheFile(spec)
|
||||
instance.__dict__[self.attname] = file
|
||||
return file
|
||||
|
|
|
|||
|
|
@ -3,13 +3,11 @@ from django.db.models.fields.files import ImageFieldFile
|
|||
from hashlib import md5
|
||||
import os
|
||||
import pickle
|
||||
from ..exceptions import UnknownExtensionError
|
||||
from ..files import GeneratedImageCacheFile, IKContentFile
|
||||
from ..files import GeneratedImageCacheFile
|
||||
from ..imagecache.backends import get_default_image_cache_backend
|
||||
from ..imagecache.strategies import StrategyWrapper
|
||||
from ..processors import ProcessorPipeline
|
||||
from ..utils import (open_image, extension_to_format, img_to_fobj,
|
||||
suggest_extension)
|
||||
from ..utils import open_image, img_to_fobj, suggest_extension
|
||||
from ..registry import generator_registry, register
|
||||
|
||||
|
||||
|
|
@ -38,7 +36,7 @@ class BaseImageSpec(object):
|
|||
|
||||
"""
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
def __init__(self):
|
||||
self.image_cache_backend = self.image_cache_backend or get_default_image_cache_backend()
|
||||
self.image_cache_strategy = StrategyWrapper(self.image_cache_strategy)
|
||||
|
||||
|
|
@ -85,10 +83,9 @@ class ImageSpec(BaseImageSpec):
|
|||
|
||||
"""
|
||||
|
||||
def __init__(self, source, **kwargs):
|
||||
def __init__(self, source):
|
||||
self.source = source
|
||||
self.processors = self.processors or []
|
||||
self.kwargs = kwargs
|
||||
super(ImageSpec, self).__init__()
|
||||
|
||||
@property
|
||||
|
|
@ -130,7 +127,6 @@ class ImageSpec(BaseImageSpec):
|
|||
def get_hash(self):
|
||||
return md5(pickle.dumps([
|
||||
self.source.name,
|
||||
self.kwargs,
|
||||
self.processors,
|
||||
self.format,
|
||||
self.options,
|
||||
|
|
@ -140,9 +136,7 @@ class ImageSpec(BaseImageSpec):
|
|||
def generate(self):
|
||||
# TODO: Move into a generator base class
|
||||
# TODO: Factor out a generate_image function so you can create a generator and only override the PIL.Image creating part. (The tricky part is how to deal with original_format since generator base class won't have one.)
|
||||
source = self.source
|
||||
filename = self.kwargs.get('filename')
|
||||
img = open_image(source)
|
||||
img = open_image(self.source)
|
||||
original_format = img.format
|
||||
|
||||
# Run the processors
|
||||
|
|
@ -150,22 +144,8 @@ class ImageSpec(BaseImageSpec):
|
|||
img = ProcessorPipeline(processors or []).process(img)
|
||||
|
||||
options = dict(self.options or {})
|
||||
|
||||
# Determine the format.
|
||||
format = self.format
|
||||
if filename and not format:
|
||||
# Try to guess the format from the extension.
|
||||
extension = os.path.splitext(filename)[1].lower()
|
||||
if extension:
|
||||
try:
|
||||
format = extension_to_format(extension)
|
||||
except UnknownExtensionError:
|
||||
pass
|
||||
format = format or img.format or original_format or 'JPEG'
|
||||
|
||||
imgfile = img_to_fobj(img, format, **options)
|
||||
# TODO: Is this the right place to wrap the file? Can we use a mixin instead? Is it even still having the desired effect? Re: #111
|
||||
content = IKContentFile(filename, imgfile.read(), format=format)
|
||||
format = self.format or img.format or original_format or 'JPEG'
|
||||
content = img_to_fobj(img, format, **options)
|
||||
return content
|
||||
|
||||
|
||||
|
|
@ -230,7 +210,7 @@ class SpecHost(object):
|
|||
self.spec_id = id
|
||||
register.generator(id, self._original_spec)
|
||||
|
||||
def get_spec(self, **kwargs):
|
||||
def get_spec(self, source):
|
||||
"""
|
||||
Look up the spec by the spec id. We do this (instead of storing the
|
||||
spec as an attribute) so that users can override apps' specs--without
|
||||
|
|
@ -240,4 +220,4 @@ class SpecHost(object):
|
|||
"""
|
||||
if not getattr(self, 'spec_id', None):
|
||||
raise Exception('Object %s has no spec id.' % self)
|
||||
return generator_registry.get(self.spec_id, **kwargs)
|
||||
return generator_registry.get(self.spec_id, source=source)
|
||||
|
|
|
|||
|
|
@ -2,9 +2,11 @@ import logging
|
|||
import os
|
||||
import mimetypes
|
||||
import sys
|
||||
from tempfile import NamedTemporaryFile
|
||||
import types
|
||||
|
||||
from django.core.exceptions import ImproperlyConfigured
|
||||
from django.core.files import File
|
||||
from django.db.models.loading import cache
|
||||
from django.utils.functional import wraps
|
||||
from django.utils.importlib import import_module
|
||||
|
|
@ -382,3 +384,41 @@ def get_logger(logger_name='imagekit', add_null_handler=True):
|
|||
if add_null_handler:
|
||||
logger.addHandler(logging.NullHandler())
|
||||
return logger
|
||||
|
||||
|
||||
def get_field_info(field_file):
|
||||
"""
|
||||
A utility for easily extracting information about the host model from a
|
||||
Django FileField (or subclass). This is especially useful for when you want
|
||||
to alter processors based on a property of the source model. For example::
|
||||
|
||||
class MySpec(ImageSpec):
|
||||
def __init__(self, source):
|
||||
instance, attname = get_field_info(source)
|
||||
self.processors = [SmartResize(instance.thumbnail_width,
|
||||
instance.thumbnail_height)]
|
||||
|
||||
"""
|
||||
return (
|
||||
getattr(field_file, 'instance', None),
|
||||
getattr(getattr(field_file, 'field', None), 'attname', None),
|
||||
)
|
||||
|
||||
|
||||
def generate(generator):
|
||||
"""
|
||||
Calls the ``generate()`` method of a generator instance, and then wraps the
|
||||
result in a Django File object so Django knows how to save it.
|
||||
|
||||
"""
|
||||
content = generator.generate()
|
||||
|
||||
# If the file doesn't have a name, Django will raise an Exception while
|
||||
# trying to save it, so we create a named temporary file.
|
||||
if not getattr(content, 'name', None):
|
||||
f = NamedTemporaryFile()
|
||||
f.write(content.read())
|
||||
f.seek(0)
|
||||
content = f
|
||||
|
||||
return File(content)
|
||||
|
|
|
|||
|
|
@ -1,9 +1,12 @@
|
|||
from django.db import models
|
||||
|
||||
from imagekit.models import ProcessedImageField
|
||||
from imagekit.models import ImageSpecField
|
||||
from imagekit.processors import Adjust
|
||||
from imagekit.processors import ResizeToFill
|
||||
from imagekit.processors import SmartCrop
|
||||
from imagekit.processors import Adjust, ResizeToFill, SmartCrop
|
||||
|
||||
|
||||
class ImageModel(models.Model):
|
||||
image = models.ImageField(upload_to='b')
|
||||
|
||||
|
||||
class Photo(models.Model):
|
||||
|
|
@ -18,6 +21,11 @@ class Photo(models.Model):
|
|||
format='JPEG', options={'quality': 90})
|
||||
|
||||
|
||||
class ProcessedImageFieldModel(models.Model):
|
||||
processed = ProcessedImageField([SmartCrop(50, 50)], format='JPEG',
|
||||
options={'quality': 90}, upload_to='p')
|
||||
|
||||
|
||||
class AbstractImageModel(models.Model):
|
||||
original_image = models.ImageField(upload_to='photos')
|
||||
abstract_class_spec = ImageSpecField()
|
||||
|
|
|
|||
36
tests/test_fields.py
Normal file
36
tests/test_fields.py
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
from django import forms
|
||||
from django.core.files.base import File
|
||||
from django.core.files.uploadedfile import SimpleUploadedFile
|
||||
from imagekit import forms as ikforms
|
||||
from imagekit.processors import SmartCrop
|
||||
from nose.tools import eq_
|
||||
from . import imagespecs # noqa
|
||||
from .models import ProcessedImageFieldModel, ImageModel
|
||||
from .utils import get_image_file
|
||||
|
||||
|
||||
def test_model_processedimagefield():
|
||||
instance = ProcessedImageFieldModel()
|
||||
file = File(get_image_file())
|
||||
instance.processed.save('whatever.jpeg', file)
|
||||
instance.save()
|
||||
|
||||
eq_(instance.processed.width, 50)
|
||||
eq_(instance.processed.height, 50)
|
||||
|
||||
|
||||
def test_form_processedimagefield():
|
||||
class TestForm(forms.ModelForm):
|
||||
image = ikforms.ProcessedImageField(spec_id='tests:testform_image',
|
||||
processors=[SmartCrop(50, 50)], format='JPEG')
|
||||
|
||||
class Meta:
|
||||
model = ImageModel
|
||||
|
||||
upload_file = get_image_file()
|
||||
file_dict = {'image': SimpleUploadedFile('abc.jpg', upload_file.read())}
|
||||
form = TestForm({}, file_dict)
|
||||
instance = form.save()
|
||||
|
||||
eq_(instance.image.width, 50)
|
||||
eq_(instance.image.height, 50)
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
from bs4 import BeautifulSoup
|
||||
import os
|
||||
from django.conf import settings
|
||||
from django.core.files.base import ContentFile
|
||||
from django.core.files import File
|
||||
from django.template import Context, Template
|
||||
from imagekit.lib import Image, StringIO
|
||||
import pickle
|
||||
|
|
@ -26,10 +26,8 @@ def create_image():
|
|||
|
||||
def create_instance(model_class, image_name):
|
||||
instance = model_class()
|
||||
img = get_image_file()
|
||||
file = ContentFile(img.read())
|
||||
instance.original_image = file
|
||||
instance.original_image.save(image_name, file)
|
||||
img = File(get_image_file())
|
||||
instance.original_image.save(image_name, img)
|
||||
instance.save()
|
||||
img.close()
|
||||
return instance
|
||||
|
|
|
|||
Loading…
Reference in a new issue