Search code examples
djangosorl-thumbnail

How to make a thumbnail of a static image using sorl-thumbnail


I have a user model with an avatar field in my Django project:

class User(AbstractBaseUser):

    avatar = models.ImageField(
        null=True,
        blank=True,
        upload_to='user/avatar/',
    )

The avatar is not required, so I would like to use a default image if a user hasn't uploaded one. On the other hand I don't want to use a default parameter in order to be able to change the default avatar in the future for all users:

class User(AbstractBaseUser):

    avatar = models.ImageField(
        null=True,
        blank=True,
        default='defaults/no-avatar.png',
        upload_to='user/avatar/',
    )

So I ended up writing a get_avatar method that returns an image if the avatar exists or a path to a default static image:

@property
def get_avatar(self):
    if self.avatar:
        return self.avatar
    return '{0}defaults/no-avatar.png'.format(settings.STATIC_URL)

But in this case sorl-thumbnail doesn't generate a thumbnail for a default image

{% thumbnail user.get_avatar "46x46" crop="center" as im %}
    <img title="{{ user }}" src="{{ im.url }}" class="img-circle" />
{% endthumbnail %}

and returns the following error:

ERROR 2014-09-22 12:49:48,020 thumbnail :: Thumbnail tag failed:
Traceback (most recent call last):
  File "/Users/vera/.virtualenvs/my_app/lib/python2.7/site-packages/sorl/thumbnail/templatetags/thumbnail.py", line 45, in render
    return self._render(context)
  File "/Users/vera/.virtualenvs/my_app/lib/python2.7/site-packages/sorl/thumbnail/templatetags/thumbnail.py", line 97, in _render
    file_, geometry, **options
  File "/Users/vera/.virtualenvs/my_app/lib/python2.7/site-packages/sorl/thumbnail/base.py", line 56, in get_thumbnail
    source_image = default.engine.get_image(source)
  File "/Users/vera/.virtualenvs/my_app/lib/python2.7/site-packages/sorl/thumbnail/engines/pil_engine.py", line 12, in get_image
    buf = StringIO(source.read())
  File "/Users/vera/.virtualenvs/my_app/lib/python2.7/site-packages/sorl/thumbnail/images.py", line 121, in read
    return self.storage.open(self.name).read()
  File "/Users/vera/.virtualenvs/my_app/lib/python2.7/site-packages/django/core/files/storage.py", line 33, in open
    return self._open(name, mode)
  File "/Users/vera/.virtualenvs/my_app/lib/python2.7/site-packages/django/core/files/storage.py", line 159, in _open
    return File(open(self.path(name), mode))
  File "/Users/vera/.virtualenvs/my_app/lib/python2.7/site-packages/django/core/files/storage.py", line 260, in path
    raise SuspiciousFileOperation("Attempted access to '%s' denied." % name)

I tried to rewrite that method in order to return an image instead:

from django.core.files.images import ImageFile

@property
def get_avatar(self):
    if self.avatar:
        return self.avatar
    return ImageFile(open(os.path.join(settings.STATIC_ROOT, 'defaults/no-avatar.png'), 'r'))

but I got a similar error:

ERROR 2014-09-22 12:52:18,448 thumbnail :: Thumbnail tag failed:
Traceback (most recent call last):
  File "/Users/vera/.virtualenvs/my_app/lib/python2.7/site-packages/sorl/thumbnail/templatetags/thumbnail.py", line 45, in render
    return self._render(context)
  File "/Users/vera/.virtualenvs/my_app/lib/python2.7/site-packages/sorl/thumbnail/templatetags/thumbnail.py", line 97, in _render
    file_, geometry, **options
  File "/Users/vera/.virtualenvs/my_app/lib/python2.7/site-packages/sorl/thumbnail/base.py", line 56, in get_thumbnail
    source_image = default.engine.get_image(source)
  File "/Users/vera/.virtualenvs/my_app/lib/python2.7/site-packages/sorl/thumbnail/engines/pil_engine.py", line 12, in get_image
    buf = StringIO(source.read())
  File "/Users/vera/.virtualenvs/my_app/lib/python2.7/site-packages/sorl/thumbnail/images.py", line 121, in read
    return self.storage.open(self.name).read()
  File "/Users/vera/.virtualenvs/my_app/lib/python2.7/site-packages/django/core/files/storage.py", line 33, in open
    return self._open(name, mode)
  File "/Users/vera/.virtualenvs/my_app/lib/python2.7/site-packages/django/core/files/storage.py", line 159, in _open
    return File(open(self.path(name), mode))
  File "/Users/vera/.virtualenvs/my_app/lib/python2.7/site-packages/django/core/files/storage.py", line 260, in path
    raise SuspiciousFileOperation("Attempted access to '%s' denied." % name)
SuspiciousFileOperation: Attempted access to '/Users/vera/workspace/my-website/static/defaults/no-avatar.png' denied.

Solution

  • sorl-thumbnail used storage functionality. Django delegates decisions about how and where to store files to a file storage system. This is the object that actually understands things like file systems, opening and reading files, etc. Django ships with a django.core.files.storage.FileSystemStorage class which implements basic local filesystem file storage. By default, Django stores files locally, using the MEDIA_ROOT and MEDIA_URL settings.

    So in your case django try find image under MEDIA_ROOT, but you have saved the image under STATIC_ROOT.

    How fix that

    As hotfix you can try move image under MEDIA_ROOT and will change path in get_avatar method. Or you can try write custom storage, which will work with both folders. Example custom storage.