Giter Site home page Giter Site logo

easy-thumbnails's Introduction

Easy Thumbnails

Build Status

A powerful, yet easy to implement thumbnailing application for Django 2.2+

Below is a quick summary of usage. For more comprehensive information, view the full documentation online or the peruse the project's docs directory.

Breaking News

Version 2.8.0 adds support for thumbnailing SVG images when installed with the [svg] extra.

Of course it doesn't make sense to thumbnail SVG images, because being in vector format they can scale to any size without quality of loss. However, users of easy-thumbnails may want to upload and use SVG images just as if they would be PNG, GIF or JPEG. They don't necessarily care about the format and definitely don't want to convert them to a pixel based format. What they want is to reuse their templates with the templatetag thumbnail and scale and crop the images to whatever their <img src="..." width="..." height="..."> has been prepared for.

This is done by adding an emulation layer named VIL, which aims to be compatible with the PIL library. All thumbnailing operations, such as scaling and cropping behave like pixel based images. The final filesize of such thumbnailed SVG images doesn't of course change, but their width/height and bounding box may be adjusted to reflect the desired size of the thumbnailed image.

Note

This feature is new and experimental, hence feedback about its proper functioning in third parts applications is highly appreciated.

Installation

Run pip install easy-thumbnails.

Add easy_thumbnails to your INSTALLED_APPS setting:

INSTALLED_APPS = (
    ...
    'easy_thumbnails',
)

Run manage.py migrate easy_thumbnails.

Example usage

Thumbnail options can be predefined in settings.THUMBNAIL_ALIASES or just specified in the template or Python code when run.

Using a predefined alias

Given the following setting:

THUMBNAIL_ALIASES = {
    '': {
        'avatar': {'size': (50, 50), 'crop': True},
    },
}

Template:

{% load thumbnail %}
<img src="{{ profile.photo|thumbnail_url:'avatar' }}" alt="" />

Python:

from easy_thumbnails.files import get_thumbnailer
thumb_url = get_thumbnailer(profile.photo)['avatar'].url

Manually specifying size / options

Template:

{% load thumbnail %}
<img src="{% thumbnail profile.photo 50x50 crop %}" alt="" />

Python:

from easy_thumbnails.files import get_thumbnailer
options = {'size': (100, 100), 'crop': True}
thumb_url = get_thumbnailer(profile.photo).get_thumbnail(options).url

Using in combination with other thumbnailers

Alternatively, you load the templatetags by {% load easy_thumbnails_tags %} instead of traditional {% load thumbnail %}. It's especially useful in projects that do make use of multiple thumbnailer libraries that use the same name (thumbnail) for the templatetag module:

{% load easy_thumbnails_tags %}
<img src="{% thumbnail profile.photo 50x50 crop %}" alt="" />

Fields

You can use ThumbnailerImageField (or ThumbnailerField) for easier access to retrieve or generate thumbnail images.

For example:

from easy_thumbnails.fields import ThumbnailerImageField

class Profile(models.Model):
    user = models.OneToOneField('auth.User')
    photo = ThumbnailerImageField(upload_to='photos', blank=True)

Accessing the field's predefined alias in a template:

{% load thumbnail %}
<img src="{{ profile.photo.avatar.url }}" alt="" />

Accessing the field's predefined alias in Python code:

thumb_url = profile.photo['avatar'].url

Thumbnail options

crop

Before scaling the image down to fit within the size bounds, it first cuts the edges of the image to match the requested aspect ratio.

Use crop="smart" to try to keep the most interesting part of the image,

Use crop="0,10" to crop from the left edge and a 10% offset from the top edge. Crop from a single edge by leaving dimension empty (e.g. crop=",0"). Offset from the right / bottom by using negative numbers (e.g., crop="-0,-10").

Often used with the upscale option, which will allow enlarging of the image during scaling.

quality=XX

Changes the quality of the output JPEG thumbnail. Defaults to 85.

In Python code, this is given as a separate option to the get_thumbnail method rather than just alter the other

Other options

Valid thumbnail options are determined by the "thumbnail processors" installed.

See the reference documentation for a complete list of options provided by the default thumbnail processors.

easy-thumbnails's People

Contributors

aarcro avatar akx avatar barseghyanartur avatar camilonova avatar danielsokolowski avatar dpnova avatar drmeers avatar gjb83 avatar jaap3 avatar jelenak avatar jezdez avatar joehybird avatar jrief avatar mogost avatar passy avatar ricky-sb avatar rofrankel avatar samluescher avatar sandipagarwal avatar selwin avatar siecje avatar smacker avatar smileychris avatar stefanfoulis avatar stefanw avatar timgates42 avatar timgraham avatar treyhunner avatar vesatoivonen avatar vicalloy avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

easy-thumbnails's Issues

Thumbnail filename

Hi.

I create image with something like this:
{% thumbnail author.images.all.0.image 100x50 %}

... and I get:
/static/images/authors/thumbs/QQbo72SE.jpg.100x50_q85.jpg

Shouldn't I get something like:
/static/images/players/thumbs/QQbo72SE_100x50_q85.jpg

... without first .jpg.?

(I check before if there are images present)

Thank you,

Kind regards.

Fix low level example in README

Imports are incorrect

recommend get_thumbnailer instead of using Thumbnailer instance directly: For pythonic thumbnail generation, the easy way is to create your thumbnailer instance with get_thumbnailer.

Tests fail if `THUMBNAIL_BASEDIR` is set

======================================================================
FAIL: testTag (easy_thumbnails.tests.templatetags.ThumbnailTagTest)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/.../easy_thumbnails/tests/templatetags.py", line 142, in testTag
    self.verify_thumbnail((240, 180), expected)
  File "/.../easy_thumbnails/tests/templatetags.py", line 50, in verify_thumbnail
    'Thumbnail file %r not found' % expected_filename)
AssertionError: Thumbnail file 'thumbs/test.jpg.240x240_q85.jpg' not found

======================================================================
FAIL: test_delete (easy_thumbnails.tests.fields.ThumbnailerFieldTest)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/.../easy_thumbnails/tests/fields.py", line 44, in test_delete
    self.assertEqual(len(self.storage.listdir('avatars')[1]), 4)
AssertionError: 1 != 4

======================================================================
FAIL: test_delete_thumbnails (easy_thumbnails.tests.fields.ThumbnailerFieldTest)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/.../easy_thumbnails/tests/fields.py", line 53, in test_delete_thumbnails
    self.assertEqual(len(self.storage.listdir('avatars')[1]), 4)
AssertionError: 1 != 4

----------------------------------------------------------------------
Ran 13 tests in 3.778s

Caught NameError while rendering: global name 'source' is not defined

Latest verison in repo.
Looks like in file:
easy_thumbnails/files.py
error in :
351 def _image(self):

Environment:
Django Version: 1.2.2
Python Version: 2.6.5

52 :

  1.         result = node.render(context)
    
    File "/home/vprohoda/sites/sp2all/sp2allve/lib/python2.6/site-packages/easy_thumbnails/templatetags/thumbnail.py" in render
  2.         thumbnail = get_thumbnailer(source).get_thumbnail(opts)
    
    File "/home/vprohoda/sites/sp2all/sp2allve/lib/python2.6/site-packages/easy_thumbnails/files.py" in get_thumbnail
  3.     thumbnail = self.generate_thumbnail(thumbnail_options)
    
    File "/home/vprohoda/sites/sp2all/sp2allve/lib/python2.6/site-packages/easy_thumbnails/files.py" in generate_thumbnail
  4.     thumbnail_image = engine.process_image(self.image, thumbnail_options)
    
    File "/home/vprohoda/sites/sp2all/sp2allve/lib/python2.6/site-packages/easy_thumbnails/files.py" in _image
  5.         self._cached_image = engine.generate_source_image(source,
    

Exception Type: TemplateSyntaxError at /sp/15/edit/offers/2002
Exception Value: Caught NameError while rendering: global name 'source' is not defined

Cannot handle gif or png images from CloudFiles

Hi Chris,

I'm using easy thumbnails with Rackspace CloudFiles and for jpeg images it works very well.

The problem is when I deal with gif or png images. I dig into the problem and found that PIL expects a file like object that support tell and seek methods and the Storage object from cloudfiles API do not have these methods.

It seems that the jpeg library do not use these methods but gif and png lib does.

The problem is when opening the image on the "_image" method of Thumbnailer class on the files module:

    self._cached_image = Image.open(self)

Do you faced similar problem? I don't know if rackspace cloudfiles api can have the tell and seek methods. Maybe the file can be opened a different way in this case, what do you think?

Unable to pickle Cloud Files storage object from django_storages

My model looks like this:

from easy_thumbnails.fields import ThumbnailerImageField
from backends.mosso import CloudFilesStorage, cloudfiles_upload_to

cloudfiles_storage = CloudFilesStorage()

class CloudFilesImage(models.Model):
    image = ThumbnailerImageField(storage=cloudfiles_storage,
                                  upload_to=cloudfiles_upload_to)

Here is the traceback:

Environment:

Request Method: POST
Request URL: http://127.0.0.1:8000/admin/images/cloudfilesimage/add/
Django Version: 1.1.1
Python Version: 2.5.2
Installed Applications:
['django.contrib.auth',
 'django.contrib.admin',
 'django.contrib.contenttypes',
 'django.contrib.sessions',
 'django.contrib.sites',
 'easy_thumbnails',
 'images']
Installed Middleware:
('django.middleware.common.CommonMiddleware',
 'django.contrib.sessions.middleware.SessionMiddleware',
 'django.contrib.auth.middleware.AuthenticationMiddleware')


Traceback:
File "/home/pete/sandbox/cloudfiles/lib/python2.5/site-packages/django/core/handlers/base.py" in get_response
  92.                 response = callback(request, *callback_args, **callback_kwargs)
File "/home/pete/sandbox/cloudfiles/lib/python2.5/site-packages/django/contrib/admin/sites.py" in root
  490.                 return self.model_page(request, *url.split('/', 2))
File "/home/pete/sandbox/cloudfiles/lib/python2.5/site-packages/django/views/decorators/cache.py" in _wrapped_view_func
  44.         response = view_func(request, *args, **kwargs)
File "/home/pete/sandbox/cloudfiles/lib/python2.5/site-packages/django/contrib/admin/sites.py" in model_page
  509.         return admin_obj(request, rest_of_url)
File "/home/pete/sandbox/cloudfiles/lib/python2.5/site-packages/django/contrib/admin/options.py" in __call__
  1092.             return self.add_view(request)
File "/home/pete/sandbox/cloudfiles/lib/python2.5/site-packages/django/db/transaction.py" in _commit_on_success
  240.                 res = func(*args, **kw)
File "/home/pete/sandbox/cloudfiles/lib/python2.5/site-packages/django/contrib/admin/options.py" in add_view
  734.                 self.save_model(request, new_object, form, change=False)
File "/home/pete/sandbox/cloudfiles/lib/python2.5/site-packages/django/contrib/admin/options.py" in save_model
  557.         obj.save()
File "/home/pete/sandbox/cloudfiles/lib/python2.5/site-packages/django/db/models/base.py" in save
  410.         self.save_base(force_insert=force_insert, force_update=force_update)
File "/home/pete/sandbox/cloudfiles/lib/python2.5/site-packages/django/db/models/base.py" in save_base
  483.                     values = [(f, f.get_db_prep_save(raw and getattr(self, f.attname) or f.pre_save(self, True))) for f in meta.local_fields if not isinstance(f, AutoField)]
File "/home/pete/sandbox/cloudfiles/lib/python2.5/site-packages/django/db/models/fields/files.py" in pre_save
  252.             file.save(file.name, file, save=False)
File "/home/pete/sandbox/cloudfiles/src/easy-thumbnails/easy_thumbnails/files.py" in save
  376.                                                     **kwargs)
File "/home/pete/sandbox/cloudfiles/src/easy-thumbnails/easy_thumbnails/files.py" in save
  337.         self.get_source_cache(create=True, update=True)
File "/home/pete/sandbox/cloudfiles/src/easy-thumbnails/easy_thumbnails/files.py" in get_source_cache
  290.             storage=self.source_storage, name=self.name)
File "/home/pete/sandbox/cloudfiles/src/easy-thumbnails/easy_thumbnails/models.py" in get_file
  23.             storage = Storage.objects.get_storage(storage)
File "/home/pete/sandbox/cloudfiles/src/easy-thumbnails/easy_thumbnails/models.py" in get_storage
  11.         pickled = pickle.dumps(storage)
File "/usr/lib/python2.5/pickle.py" in dumps
  1366.     Pickler(file, protocol).dump(obj)
File "/usr/lib/python2.5/pickle.py" in dump
  224.         self.save(obj)
File "/usr/lib/python2.5/pickle.py" in save
  331.         self.save_reduce(obj=obj, *rv)
File "/usr/lib/python2.5/pickle.py" in save_reduce
  419.             save(state)
File "/usr/lib/python2.5/pickle.py" in save
  286.             f(self, obj) # Call unbound method with explicit self
File "/usr/lib/python2.5/pickle.py" in save_dict
  649.         self._batch_setitems(obj.iteritems())
File "/usr/lib/python2.5/pickle.py" in _batch_setitems
  663.                 save(v)
File "/usr/lib/python2.5/pickle.py" in save
  331.         self.save_reduce(obj=obj, *rv)
File "/usr/lib/python2.5/pickle.py" in save_reduce
  419.             save(state)
File "/usr/lib/python2.5/pickle.py" in save
  286.             f(self, obj) # Call unbound method with explicit self
File "/usr/lib/python2.5/pickle.py" in save_dict
  649.         self._batch_setitems(obj.iteritems())
File "/usr/lib/python2.5/pickle.py" in _batch_setitems
  663.                 save(v)
File "/usr/lib/python2.5/pickle.py" in save
  286.             f(self, obj) # Call unbound method with explicit self
File "/usr/lib/python2.5/pickle.py" in save_inst
  725.         save(stuff)
File "/usr/lib/python2.5/pickle.py" in save
  286.             f(self, obj) # Call unbound method with explicit self
File "/usr/lib/python2.5/pickle.py" in save_dict
  649.         self._batch_setitems(obj.iteritems())
File "/usr/lib/python2.5/pickle.py" in _batch_setitems
  663.                 save(v)
File "/usr/lib/python2.5/pickle.py" in save
  286.             f(self, obj) # Call unbound method with explicit self
File "/usr/lib/python2.5/pickle.py" in save_inst
  725.         save(stuff)
File "/usr/lib/python2.5/pickle.py" in save
  286.             f(self, obj) # Call unbound method with explicit self
File "/usr/lib/python2.5/pickle.py" in save_dict
  649.         self._batch_setitems(obj.iteritems())
File "/usr/lib/python2.5/pickle.py" in _batch_setitems
  663.                 save(v)
File "/usr/lib/python2.5/pickle.py" in save
  306.                 rv = reduce(self.proto)
File "/usr/lib/python2.5/copy_reg.py" in _reduce_ex
  76.             raise TypeError("a class that defines __slots__ without "

Exception Type: TypeError at /admin/images/cloudfilesimage/add/
Exception Value: a class that defines __slots__ without defining __getstate__ cannot be pickled

Preserve fileformat

It would be wonderful to have the ability to preserve the file format when thumbnailing. As far as I can see currently the THUMBNAIL_EXTENSION is used to determine the output format. Now if I have at least one PNG file with transparency which I want to thumbnail, I have to set the output format to PNG for all files which is not ideal since PNGs usually are bigger than jpg files.

My proposal would be to allow setting THUMBNAIL_EXTENSION to None to tell easy-thumbnails to just preserve the format.

ImportError fix...

Sometimes (usually if you install PIL via easy_install I think) the Python Imaging Lib gets installed in the root namespace so doing from PIL import Image is not working...

Basically I searched/replaced every instance of from PIL import Image with:

try:
    from PIL import Image
except ImportError:
    import Image

and so far it appears to have successfully fixed the issue... (that issue is also discussed in the django bug tracker here: http://code.djangoproject.com/ticket/6054 ) I think they came to the same conclusion...

Migration 0010 fails, when using MySQL

Hey,

the south migration with mysql fails with the current alpha-12 version.

easy_thumbnails:0010_rename_storage
! Error found during real run of migration! Aborting.

[...]
File "/home/daniel/.virtualenvs/myenv/lib/python2.6/site-packages/easy_thumbnails/migrations/0010_rename_storage.py", line 13, in forwards
db.drop_foreign_key('easy_thumbnails_source', 'storage_new_id')
[...]
raise ValueError("Cannot find a FOREIGN KEY constraint on table %s, column %s" % (table_name, column))
ValueError: Cannot find a FOREIGN KEY constraint on table easy_thumbnails_source, column storage_new_id

First time {% thumbnail .... %} is called it fails with IntegrityError

IntegrityError: null value in column "storage_id" violates not-null constraint

exception is raised (with THUMBNAIL_DEBUG = True) when first attempt to create thumbnail is made. This is due to lack of 'storage' in kwargs passed to get_or_create in models.py->FileManager->get_file

Traceback:

File "/opt/path/to/my/app/parts/easy-thumbnails/easy_thumbnails/templatetags/thumbnail.py", line 80, in render
  thumbnail = get_thumbnailer(source).get_thumbnail(opts)
File "/opt/path/to/my/app/parts/easy-thumbnails/easy_thumbnails/files.py", line 334, in get_thumbnail
  self.get_thumbnail_cache(filename, create=True, update=True)
File "/opt/path/to/my/app/parts/easy-thumbnails/easy_thumbnails/files.py", line 376, in get_thumbnail_cache
  source = self.get_source_cache(create=True)
File "/opt/path/to/my/app/parts/easy-thumbnails/easy_thumbnails/files.py", line 369, in get_source_cache
  storage=self.source_storage, name=self.name)
File "/opt/path/to/my/app/parts/easy-thumbnails/easy_thumbnails/models.py", line 16, in get_file
  object, created = self.get_or_create(**kwargs)
File "/opt/path/to/my/app/parts/django/django/db/models/manager.py", line 123, in get_or_create
  return self.get_query_set().get_or_create(**kwargs)
File "/opt/path/to/my/app/parts/django/django/db/models/query.py", line 343, in get_or_create
    raise e
IntegrityError: null value in column "storage_id" violates not-null constraint

Corresponding SQL is:

INSERT INTO "easy_thumbnails_source" ("storage_hash", "name", "modified") VALUES (E'df2a8819fc7f348e9715cca951e8e5df', E'uploads/PA035971_.jpg', E'2010-10-05 21:43:

23.439094')

Remove the storage class

It's a pain, and an edge case which has caused headaches and complex code.

Replace with a hash of the storage class module name as a CharField on the File base model class (this will be on the Source and Thumbnail models then).

Low-Level Usage examples don't seem to work

I get ImportErrors with from easy_thumbnails import Thumbnailer and from easy_thumbnails import ThumbnailFile -- had to update to from easy_thumbnails.files import ...

Also,

source = ThumbnailFile('animals/aarvark.jpg')
square_thumbnail(source)

Gives me:

Traceback (most recent call last):
...
  File ".../easy_thumbnails/files.py", line 318, in get_thumbnail
    thumbnail = self.generate_thumbnail(thumbnail_options)
  File ".../easy_thumbnails/files.py", line 234, in generate_thumbnail
    thumbnail_image = engine.process_image(self.image, thumbnail_options)
  File ".../easy_thumbnails/files.py", line 356, in _image
    self.open()
  File ".../django/core/files/base.py", line 110, in open
    raise ValueError("The file cannot be reopened.")
ValueError: The file cannot be reopened.

Any plans on storing the image dimensions in the easy_thumbnails models?

As we use the remote storage (CloudFiles) for everything, the easy_thumbnails is only providing the image URLs to the templates and we lose the width/height info, so the designers can't create an accurate placeholders.

My app is surely capable of using PIL to ping the dimensions of source images and store them in its own models, but there seems to be no easy way to do the same with the thumbnails themselves.
And it would be cleaner to handle the dimensions in one place.

Would you see the support for remote-storage image dimensions:

  • proper for general e_t (yours repo)
  • or should I add the support to my clone and sync it with your future commits
  • or this would be best accomplished from the app itself?

Thank you

Transparent thumbnails with remote storage backends

I was unable to get thumbnails working for any images that had transparency when using the django-storages s3boto backend. They would work fine on initial generation, but then subsequent loads (when easy-thumbnails was using the cache) would fail.

I added some unit tests trying to pinpoint the problem and I think I might have figured it out with this commit: http://github.com/winhamwr/easy-thumbnails/commit/d1e74f2213909cad39a307cdbda078fa4b4fa21e

I also have some other unit test refactoring/additions in the branch if you're interested. The defaultfs flavor of tests might not be worth adding, but I needed a way to specifically test with my s3 backend while I was trying to mock out the FakeRemoteStorage backend and I figure it might be useful for quickly testing different backends. I didn't add it to easy-thumbnails.tests so I think the default test runner won't pick them up anyway.

Add dependency to Django >= 1.2

easy-thumbnails, as an emerging library, should require Django 1.2+ and should be explicit about it in the setup.py file.

Wrong arguments passed to get_source_cache

diff --git a/easy_thumbnails/files.py b/easy_thumbnails/files.py
index 763ad13..f362c2c 100644
--- a/easy_thumbnails/files.py
+++ b/easy_thumbnails/files.py
@@ -334,7 +334,7 @@ class ThumbnailerFieldFile(FieldFile, Thumbnailer):
         
         """
         super(ThumbnailerFieldFile, self).save(name, content, *args, **kwargs)
-        self.get_source_cache(self, create=True, update=True)
+        self.get_source_cache(create=True, update=True)
 
     def delete(self, *args, **kwargs):
         """

can't pickle HASH objects

Been getting the following exception while using django-storages s3boto file storage backend.


Traceback:
File "/Users/gtaylor/.virtualenvs/coursebook/lib/python2.6/site-packages/django/core/handlers/base.py" in get_response
  100.                     response = callback(request, *callback_args, **callback_kwargs)
File "/Users/gtaylor/.virtualenvs/coursebook/lib/python2.6/site-packages/django/contrib/admin/options.py" in wrapper
  239.                 return self.admin_site.admin_view(view)(*args, **kwargs)
File "/Users/gtaylor/.virtualenvs/coursebook/lib/python2.6/site-packages/django/utils/decorators.py" in _wrapped_view
  76.                     response = view_func(request, *args, **kwargs)
File "/Users/gtaylor/.virtualenvs/coursebook/lib/python2.6/site-packages/django/views/decorators/cache.py" in _wrapped_view_func
  69.         response = view_func(request, *args, **kwargs)
File "/Users/gtaylor/.virtualenvs/coursebook/lib/python2.6/site-packages/django/contrib/admin/sites.py" in inner
  190.             return view(request, *args, **kwargs)
File "/Users/gtaylor/.virtualenvs/coursebook/lib/python2.6/site-packages/django/utils/decorators.py" in _wrapper
  21.             return decorator(bound_func)(*args, **kwargs)
File "/Users/gtaylor/.virtualenvs/coursebook/lib/python2.6/site-packages/django/utils/decorators.py" in _wrapped_view
  76.                     response = view_func(request, *args, **kwargs)
File "/Users/gtaylor/.virtualenvs/coursebook/lib/python2.6/site-packages/django/utils/decorators.py" in bound_func
  17.                 return func(self, *args2, **kwargs2)
File "/Users/gtaylor/.virtualenvs/coursebook/lib/python2.6/site-packages/django/db/transaction.py" in _commit_on_success
  299.                     res = func(*args, **kw)
File "/Users/gtaylor/.virtualenvs/coursebook/lib/python2.6/site-packages/django/contrib/admin/options.py" in change_view
  895.                 self.save_model(request, new_object, form, change=True)
File "/Users/gtaylor/.virtualenvs/coursebook/lib/python2.6/site-packages/django/contrib/admin/options.py" in save_model
  597.         obj.save()
File "/Users/gtaylor/.virtualenvs/coursebook/lib/python2.6/site-packages/django/db/models/base.py" in save
  435.         self.save_base(using=using, force_insert=force_insert, force_update=force_update)
File "/Users/gtaylor/.virtualenvs/coursebook/lib/python2.6/site-packages/django/db/models/base.py" in save_base
  500.                         values = [(f, None, (raw and getattr(self, f.attname) or f.pre_save(self, False))) for f in non_pks]
File "/Users/gtaylor/.virtualenvs/coursebook/lib/python2.6/site-packages/django/db/models/fields/files.py" in pre_save
  255.             file.save(file.name, file, save=False)
File "/Users/gtaylor/.virtualenvs/coursebook/lib/python2.6/site-packages/easy_thumbnails/files.py" in save
  418.                                                     **kwargs)
File "/Users/gtaylor/.virtualenvs/coursebook/lib/python2.6/site-packages/easy_thumbnails/files.py" in save
  368.         self.get_source_cache(create=True, update=True)
File "/Users/gtaylor/.virtualenvs/coursebook/lib/python2.6/site-packages/easy_thumbnails/files.py" in get_source_cache
  319.             storage=self.source_storage, name=self.name)
File "/Users/gtaylor/.virtualenvs/coursebook/lib/python2.6/site-packages/easy_thumbnails/models.py" in get_file
  23.             storage = Storage.objects.get_storage(storage)
File "/Users/gtaylor/.virtualenvs/coursebook/lib/python2.6/site-packages/easy_thumbnails/models.py" in get_storage
  11.         pickled = pickle.dumps(storage)
File "/opt/local/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/pickle.py" in dumps
  1366.     Pickler(file, protocol).dump(obj)
File "/opt/local/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/pickle.py" in dump
  224.         self.save(obj)
File "/opt/local/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/pickle.py" in save
  331.         self.save_reduce(obj=obj, *rv)
File "/opt/local/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/pickle.py" in save_reduce
  419.             save(state)
File "/opt/local/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/pickle.py" in save
  286.             f(self, obj) # Call unbound method with explicit self
File "/opt/local/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/pickle.py" in save_dict
  649.         self._batch_setitems(obj.iteritems())
File "/opt/local/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/pickle.py" in _batch_setitems
  663.                 save(v)
File "/opt/local/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/pickle.py" in save
  331.         self.save_reduce(obj=obj, *rv)
File "/opt/local/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/pickle.py" in save_reduce
  419.             save(state)
File "/opt/local/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/pickle.py" in save
  286.             f(self, obj) # Call unbound method with explicit self
File "/opt/local/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/pickle.py" in save_dict
  649.         self._batch_setitems(obj.iteritems())
File "/opt/local/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/pickle.py" in _batch_setitems
  663.                 save(v)
File "/opt/local/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/pickle.py" in save
  286.             f(self, obj) # Call unbound method with explicit self
File "/opt/local/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/pickle.py" in save_inst
  725.         save(stuff)
File "/opt/local/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/pickle.py" in save
  286.             f(self, obj) # Call unbound method with explicit self
File "/opt/local/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/pickle.py" in save_dict
  649.         self._batch_setitems(obj.iteritems())
File "/opt/local/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/pickle.py" in _batch_setitems
  663.                 save(v)
File "/opt/local/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/pickle.py" in save
  286.             f(self, obj) # Call unbound method with explicit self
File "/opt/local/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/pickle.py" in save_inst
  725.         save(stuff)
File "/opt/local/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/pickle.py" in save
  286.             f(self, obj) # Call unbound method with explicit self
File "/opt/local/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/pickle.py" in save_dict
  649.         self._batch_setitems(obj.iteritems())
File "/opt/local/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/pickle.py" in _batch_setitems
  663.                 save(v)
File "/opt/local/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/pickle.py" in save
  286.             f(self, obj) # Call unbound method with explicit self
File "/opt/local/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/pickle.py" in save_inst
  725.         save(stuff)
File "/opt/local/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/pickle.py" in save
  286.             f(self, obj) # Call unbound method with explicit self
File "/opt/local/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/pickle.py" in save_dict
  649.         self._batch_setitems(obj.iteritems())
File "/opt/local/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/pickle.py" in _batch_setitems
  663.                 save(v)
File "/opt/local/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/pickle.py" in save
  306.                 rv = reduce(self.proto)
File "/Users/gtaylor/.virtualenvs/coursebook/bin/../lib/python2.6/copy_reg.py" in _reduce_ex
  70.             raise TypeError, "can't pickle %s objects" % base.__name__

Exception Type: TypeError at /admin/profile/coursebookid/3/
Exception Value: can't pickle HASH objects

Field arguments don't work with South

I tried adding a ThumbnailerImageField to my models but there seems to be something wrong with South migrations.

I specified null=True, blank=True and max_length=1024 but none of these end up in South migration file. This might also be a South problem as it doesn't as default values anymore but lacks these in later steps.

I'm using South 0.7 with Django 1.2.

MEDIA_ROOT is used on the output of a {% thumbnail %}

When using:

{% thumbnail some_thumbnailerimagefield_instance 150x150 %}

The output is:

d:/some/path/to/my/images/directory/image.jpg.150x150_q85.jpg

Why would this be? At what point would I ever want to output the MEDIA_ROOT into my web page?

Filesize

This is a great tool, thanks!

But (always a but ;-). If I run 'Smush It', the images are all optimized up to 50%. Is there any way anyone can optimize this tool for the filesize?

Thanx in advance
Martijn

test_crop_smart test failure

test_crop_smart test fails on Python 2.6, PIL 1.1.7, easy-thumbnails master, and Django 1.2.


Installed 62 object(s) from 1 fixture(s)
....F.
======================================================================
FAIL: test_crop_smart (easy_thumbnails.tests.processors.ScaleAndCropTest)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/Users/gtaylor/Documents/workspace/coursebook/bundled/easy-thumbnails/easy_thumbnails/tests/processors.py", line 85, in test_crop_smart
    self.assertImagesEqual(smart_crop, expected)
  File "/Users/gtaylor/Documents/workspace/coursebook/bundled/easy-thumbnails/easy_thumbnails/tests/processors.py", line 19, in assertImagesEqual
    (msg or 'The two images were not identical')
AssertionError: The two images were not identical

----------------------------------------------------------------------

LazyObject fools get_thumbnailer

DefaultStorage derives from LazyObject, and so does not test true as an instance of Storage

>>> from django.core.files.storage import default_storage, Storage
>>> isinstance( default_storage, Storage )
False
>>> default_storage
<django.core.files.storage.DefaultStorage object at 0x2ac735da22d0>

Migration error

First time I tried to use the app. Migrate gives me this error:

_mysql_exceptions.OperationalError: (1170, "BLOB/TEXT column 'pickle' used in key specification without a key length")

Question - how do I create a processor?

I'm trying to make a processor that squares images in a fashion such that you specify a width, and if an image is wider than tall, the width is set to your "width" and the shorter height is centered vertically and filled with white space. The same goes for taller than wide images. I am using PIL for this functionality but I can't figure out what type to return.

I've tried to use this to find out:

def make_square(image, size):
    raise TypeError(type(image))

but no exception is output. Are exceptions completely trapped, or am I doing something wrong. I have DEBUG = TRUE in settings.py and I have "myproject.image_processors.make_square" in settings.THUMBNAIL_PROCESSORS.

Wrong dimensions

Original image:
2400px × 3620px

I call:
{% thumbnail entry.image 110x1100 sharpen quality=90 %}
I get:
109px × 165px

I call:
{% thumbnail entry.image 110x165 sharpen crop quality=90 %}
I get:
109px × 165px

There is no way to make it 110px wide :(

thumbnail_storage is None by default

In [6]: l.image.generate_thumbnail({'size':(50,50)})
---------------------------------------------------------------------------
        Traceback (most recent call last)

/home/pete/sandbox/cloudfiles/project/ in ()

/home/pete/sandbox/cloudfiles/src/easy-thumbnails/easy_thumbnails/files.py in generate_thumbnail(self, thumbnail_options)
    176         filename = self.get_thumbnail_name(thumbnail_options)
    177         thumbnail = ThumbnailFile(filename, ContentFile(data),
--> 178                                   storage=self.thumbnail_storage)
    179         thumbnail.image = thumbnail_image
    180         thumbnail._committed = False

: 'ThumbnailerImageFieldFile' object has no attribute 'thumbnail_storage'

This fixes the issue:

diff --git a/easy_thumbnails/fields.py b/easy_thumbnails/fields.py
index d49af50..ee77d2e 100644
--- a/easy_thumbnails/fields.py
+++ b/easy_thumbnails/fields.py
@@ -16,10 +16,11 @@ class ThumbnailerField(FileField):
     def __init__(self, *args, **kwargs):
         # Arguments not explicitly defined so that the normal ImageField
         # positional arguments can be used.
-        self.thumbnail_storage = kwargs.pop('thumbnail_storage', None)
         
         super(ThumbnailerField, self).__init__(*args, **kwargs)
 
+        self.thumbnail_storage = kwargs.pop('thumbnail_storage', self.storage)
+
     def south_field_triple(self):
         """
         Return a suitable description of this field for South.

Thumbnails have MIME type application/octet-stream on CloudFiles

Browsers can have problems with that.

Using latest django-storages from http://bitbucket.org/smileychris/django-storages/, latest python-cloudfiles from git://github.com/rackspace/python-cloudfiles.git, latest easy-thumbnails from git://github.com/SmileyChris/easy-thumbnails.git. As of 2010-03-08.

My proposed solution:

--- a/easy_thumbnails/files.py
+++ b/easy_thumbnails/files.py
@@ -1,23 +1,24 @@
 from PIL import Image
-from django.core.files.base import File, ContentFile
+from django.core.files.base import File
+from django.core.files.uploadedfile import SimpleUploadedFile
 from django.core.files.storage import get_storage_class, default_storage
 from django.db.models.fields.files import ImageFieldFile, FieldFile
 from django.utils.html import escape
 from django.utils.safestring import mark_safe
 from easy_thumbnails import engine, models, utils
 import datetime
 import os
-
+import mimetypes
 
 DEFAULT_THUMBNAIL_STORAGE = get_storage_class(
                                         utils.get_setting('DEFAULT_STORAGE'))()
 
 
 def get_thumbnailer(source, relative_name=None):
     """
     Get a thumbnailer for a source file.
     
     """
     if isinstance(source, Thumbnailer):
         return source
     elif isinstance(source, FieldFile):
@@ -181,27 +182,29 @@ class Thumbnailer(File):
     def generate_thumbnail(self, thumbnail_options):
         """
         Return a ``ThumbnailFile`` containing a thumbnail image.
         
         The thumbnail image is generated using the ``thumbnail_options``
         dictionary.
         
         """
         thumbnail_image = engine.process_image(self.image, thumbnail_options)
         quality = thumbnail_options.get('quality', self.thumbnail_quality)
         data = engine.save_image(thumbnail_image, quality=quality).read()
 
         filename = self.get_thumbnail_name(thumbnail_options)
-        thumbnail = ThumbnailFile(filename, ContentFile(data),
+        content_type = mimetypes.guess_type(filename)[0]
+        file = SimpleUploadedFile(filename, data, content_type)
+        thumbnail = ThumbnailFile(filename, file,
                                   storage=self.thumbnail_storage)
         thumbnail.image = thumbnail_image

files.thumnailer.get_thumbnail fails when re-saving models because it makes a hash of path stored in model

I'm trying to use easy-thumbnails to pre-generate a thumbnail for an image in one of my models.
To do so, I've created a custom save method for my model:

class Picture(models.Model):
img = models.ImageField(upload_to='pics/%Y/%B/%d/' , max_length=256,)
thumb = ThumbnailerImageField(upload_to='thumbs/%Y/%B/%d/',
resize_source = dict(size=(120,90), crop=True), blank=True, null=True, )
caption = models.CharField(max_length=512)

 def save(self):
     super(Picture, self).save()
     self.thumb = Thumbnailer(self.img.file).get_thumbnail(dict(size=(120, 90), crop=True,))
     super(Picture, self).save()

(thumb has a custom widget)
This works fine at first. But if the caption gets changed and save called again, the thumbnail field changes from a relative path to a system path, ie:
self.img.file pics/2010/June/24/xyz.jpg
becomes the thumb:
thumbs/2010/June/24/xyz.jpg
but if the model is saved again, the thumb becomes:
/home/webby/www/localhost/wc-web/media/pics/2010/June/24/thmb_xyz.jpg.120x90_q85_crop.jpg

This behavior seems to occur in get_thumbnail, based on whether the
if self.thumbnail_exists(filename):
condition triggers.

I'm still trying to figure out why it functions as expected on first save but not second.

null value in column "storage_id" violates not-null constraint

Hey there,

an upgrade from alpha 3 to alpha 12 broke some tests for me. When saving a new Thumbnail an IntegrityError is raised: http://gist.github.com/609702

The field definition is quite simple:

thumbnail_photo = ThumbnailerImageField(
    upload_to="product_photos",
    resize_source={
        'size': (128, 128),
        'quality': 85
    }
)

Am I missing something? In that case I think the error should be caught earlier.

Cheers,
Pascal

Thumbnail folder (not really an issue)

Hi.

Is it possible to save thumbnails into specific folder?

e.g. I have images in /django/images ... but want to have thumbnails in /django/images/thumbs/ ...

Kind regards,

Rok

Error migrating from scratch with mysql innodb

Migrating forward from scratch with django 1.2, easy-thumbnails 78ce430 and South 1.2 on MySQL with InnoDB fails running 0010_rename_storage and gives this traceback: http://dpaste.com/243358/

The FK error is:

LATEST FOREIGN KEY ERROR
------------------------
100914 12:57:35 Error in foreign key constraint of table dev/easy_thumbnails_source:
there is no index in the table which would contain
the columns as the first columns, or the data types in the
table do not match to the ones in the referenced table
or one of the ON ... SET NULL columns is declared NOT NULL. Constraint:,
CONSTRAINT storage_new_id_refs_id_5cebe21bafd2e07f FOREIGN KEY (storage_new_id) REFERENCES easy_thumbnails_storage (id)

Compatibility with sorl-thumbnail

This template tag is nearly a drop-in replacement for sorl.thumbnail except the name of the template tag is "thumbnails" instead of sorl.thumbnail's "thumbnail"

Is this intentional? It seems having a drop-in replacement for sorl-thumbnail would increase adoption rate.

margin filter / give Thumbnail file a margin property

the new sorl thumbnail has a margin filter, which is all very nice but requires me to duplicate the size specification.

Would be nice if the ThumbnailFile also knew the requested size so it knew what the margin had to be to make the image centered on the requested size.

Something like:

{% thumbnail model.image "100x100" as im %}
<img src="{{ im.url }}" style="margin:{{ im.margin }}" alt="Image" width="{{ im.width }}" height="{{ im.height }}" />

How to change storage in template?

I have image field:
image = models.ImageField(upload_to='programos/%Y/%m/%d', storage=uploads_storage)

storage point to other folder than MEDIA_* settings. How can I specify in template, what storage should be used?

Only constrain one size dimension

I'd like to be able to only define a size constraint for either the width or height. This allows "skyscraper" images to be scaled to fit a specific width but still remain viewable as they extend down vertically.

Right now, the only way to do this is to specify a very large width or height, such at 250x9000.

I propose that specifying 250x0 will allow the image to be as tall as it wants and only constrain the width to 250px. Likewise, setting a width of 0 will no longer constrain the width.

We're storing the same 40 bytes long storage_id hash several million times

As the primary key of Storage is the 40 bytes SHA1 hash and both Source and Thumbnail models reference it, there's a huge data redundancy and I expect also the performance loss. Why was that necessary instead of a simple integer?

The fact we're using only one type of storage (mosso CloudFiles) throughout the entire app is making it only a bit worse... :)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.