Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Store thumbnail dimensions in db to improve performance, especially w…

…hen reading files from remote storages
  • Loading branch information...
commit 29c05165722b413838a3143bdc91ec0e269f6cd5 1 parent 8661b1f
@cscheng authored
View
29 easy_thumbnails/files.py
@@ -1,4 +1,5 @@
from django.core.files.base import File, ContentFile
+from django.core.files.images import get_image_dimensions
from django.core.files.storage import get_storage_class, default_storage, \
Storage
from django.db.models.fields.files import ImageFieldFile, FieldFile
@@ -226,6 +227,32 @@ def open(self, mode=None, *args, **kwargs):
else:
return super(ThumbnailFile, self).open(mode, *args, **kwargs)
+ def _get_image_dimensions(self):
+ from numbers import Number

Hrm, do we need to rely on numbers? Django still supports Python 2.5 at the moment.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
+ if not hasattr(self, '_dimensions_cache'):
+ thumbnail = models.Thumbnail.objects.get_file(self.storage, self.name)
+ if thumbnail:
+ width = thumbnail.width
+ height = thumbnail.height
+ # retrieve image dimensions from db if possible
+ if isinstance(width, Number) and isinstance(height, Number):
+ self._dimensions_cache = (width, height)
+ else:
+ # open image and get the image dimensions via PIL
+ close = self.closed
+ self.open()
+ dimensions = get_image_dimensions(self, close=close)
+ self._dimensions_cache = dimensions
+ # store in db for future use
+ thumbnail.width = dimensions[0]
+ thumbnail.height = dimensions[1]
+ thumbnail.save()
+ else:
+ close = self.closed
+ self.open()
+ self._dimensions_cache = get_image_dimensions(self, close=close)

Being finicky, but this could be a bit dryer here. Something like

if thumbnail and thumbnail.width is not None and thumbnail.height is not None:
    self._dimensions_cache = (thumbnail.width, thumbnail.height)
else:
    # get dimensions
    if thumbnail:
        # save dimensions
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
+ return self._dimensions_cache
+
class Thumbnailer(File):
"""
@@ -380,12 +407,12 @@ def get_thumbnail(self, thumbnail_options, save=True):
thumbnail = self.generate_thumbnail(thumbnail_options)
if save:
save_thumbnail(thumbnail, self.thumbnail_storage)
- signals.thumbnail_created.send(sender=thumbnail)
# Ensure the right thumbnail name is used based on the transparency
# of the image.
filename = (utils.is_transparent(thumbnail.image) and
transparent_name or opaque_name)
self.get_thumbnail_cache(filename, create=True, update=True)
+ signals.thumbnail_created.send(sender=thumbnail)
return thumbnail
View
47 easy_thumbnails/migrations/0016_auto__add_field_thumbnail_width__add_field_thumbnail_height.py
@@ -0,0 +1,47 @@
+# encoding: utf-8
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+class Migration(SchemaMigration):
+
+ def forwards(self, orm):
+
+ # Adding field 'Thumbnail.width'
+ db.add_column('easy_thumbnails_thumbnail', 'width', self.gf('django.db.models.fields.PositiveIntegerField')(null=True, blank=True), keep_default=False)
+
+ # Adding field 'Thumbnail.height'
+ db.add_column('easy_thumbnails_thumbnail', 'height', self.gf('django.db.models.fields.PositiveIntegerField')(null=True, blank=True), keep_default=False)
+
+
+ def backwards(self, orm):
+
+ # Deleting field 'Thumbnail.width'
+ db.delete_column('easy_thumbnails_thumbnail', 'width')
+
+ # Deleting field 'Thumbnail.height'
+ db.delete_column('easy_thumbnails_thumbnail', 'height')
+
+
+ models = {
+ 'easy_thumbnails.source': {
+ 'Meta': {'unique_together': "(('storage_hash', 'name'),)", 'object_name': 'Source'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'modified': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
+ 'storage_hash': ('django.db.models.fields.CharField', [], {'max_length': '40', 'db_index': 'True'})
+ },
+ 'easy_thumbnails.thumbnail': {
+ 'Meta': {'unique_together': "(('storage_hash', 'name', 'source'),)", 'object_name': 'Thumbnail'},
+ 'height': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'modified': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
+ 'source': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'thumbnails'", 'to': "orm['easy_thumbnails.Source']"}),
+ 'storage_hash': ('django.db.models.fields.CharField', [], {'max_length': '40', 'db_index': 'True'}),
+ 'width': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'})
+ }
+ }
+
+ complete_apps = ['easy_thumbnails']
View
3  easy_thumbnails/models.py
@@ -55,6 +55,9 @@ class Source(File):
class Thumbnail(File):
source = models.ForeignKey(Source, related_name='thumbnails')
+ # store thumbnail dimensions in db, much faster than reading from remote storage
+ width = models.PositiveIntegerField(blank=True, null=True)
+ height = models.PositiveIntegerField(blank=True, null=True)
class Meta:
unique_together = (('storage_hash', 'name', 'source'),)
View
15 easy_thumbnails/signal_handlers.py
@@ -48,3 +48,18 @@ def generate_aliases_global(fieldfile, **kwargs):
# Avoids circular import.
from easy_thumbnails.files import generate_all_aliases
generate_all_aliases(fieldfile, include_global=True)
+
+
+def save_thumbnail_dimensions(sender, **kwargs):
+ """
+ Save thumbnail dimensions to the thumbnail record,
+ when a new thumbnail file has been created.
+ """
+ from easy_thumbnails import models
+ thumbnail = models.Thumbnail.objects.get_file(sender.storage, sender.name)
+ thumbnail.width = sender.width
+ thumbnail.height = sender.height
+ thumbnail.save()
+
+
+signals.thumbnail_created.connect(save_thumbnail_dimensions)

2 comments on commit 29c0516

@SmileyChris

Good job on putting this together.

@cscheng
Owner

Thanks! I also implemented the changes you suggested in _get_image_dimensions().

Please sign in to comment.
Something went wrong with that request. Please try again.