mirror of
https://github.com/jazzband/django-avatar.git
synced 2026-03-16 22:20:30 +00:00
Merge branch 'rafiqhilali-fix-cached-image-sizes'
This commit is contained in:
commit
827244ca6c
4 changed files with 46 additions and 14 deletions
|
|
@ -4,6 +4,7 @@ Changelog
|
|||
* Unreleased
|
||||
* Allowed for rectangular avatars. Custom avatar tag templates now require the specification of both a ``width`` and ``height`` attribute instead of ``size``.
|
||||
* Made ``True`` the default value of ``AVATAR_CLEANUP_DELETED``. (Set to ``False`` to obtain previous behavior).
|
||||
* Fix invalidate_cache for on-the-fly created thumbnails
|
||||
|
||||
* 6.0.1 (August 12, 2022)
|
||||
* Exclude tests folder from distribution.
|
||||
|
|
|
|||
|
|
@ -188,6 +188,7 @@ class Avatar(models.Model):
|
|||
thumb = self.avatar.storage.save(
|
||||
self.avatar_name(width, height), thumb_file
|
||||
)
|
||||
invalidate_cache(self.user, width, height)
|
||||
|
||||
def avatar_url(self, width, height=None):
|
||||
return self.avatar.storage.url(self.avatar_name(width, height))
|
||||
|
|
|
|||
|
|
@ -28,13 +28,17 @@ def get_user(userdescriptor):
|
|||
return User.objects.get_by_natural_key(userdescriptor)
|
||||
|
||||
|
||||
def get_cache_key(user_or_username, width, height, prefix):
|
||||
def get_cache_key(user_or_username, prefix, width=None, height=None):
|
||||
"""
|
||||
Returns a cache key consisten of a username and image size.
|
||||
"""
|
||||
if isinstance(user_or_username, get_user_model()):
|
||||
user_or_username = get_username(user_or_username)
|
||||
key = "%s_%s_%s_%s" % (prefix, user_or_username, width, height or width)
|
||||
key = f"{prefix}_{user_or_username}"
|
||||
if width:
|
||||
key += f"_{width}"
|
||||
if height or width:
|
||||
key += f"x{height or width}"
|
||||
return "%s_%s" % (
|
||||
slugify(key)[:100],
|
||||
hashlib.md5(force_bytes(key)).hexdigest(),
|
||||
|
|
@ -62,11 +66,16 @@ def cache_result(default_size=settings.AVATAR_DEFAULT_SIZE):
|
|||
def cached_func(user, width=None, height=None, **kwargs):
|
||||
prefix = func.__name__
|
||||
cached_funcs.add(prefix)
|
||||
key = get_cache_key(user, width or default_size, height, prefix=prefix)
|
||||
key = get_cache_key(user, prefix, width or default_size, height)
|
||||
result = cache.get(key)
|
||||
if result is None:
|
||||
result = func(user, width or default_size, height, **kwargs)
|
||||
cache_set(key, result)
|
||||
# add image size to set of cached sizes so we can invalidate them later
|
||||
sizes_key = get_cache_key(user, "cached_sizes")
|
||||
sizes = cache.get(sizes_key, set())
|
||||
sizes.add((width or default_size, height or width or default_size))
|
||||
cache_set(sizes_key, sizes)
|
||||
return result
|
||||
|
||||
return cached_func
|
||||
|
|
@ -78,16 +87,18 @@ def invalidate_cache(user, width=None, height=None):
|
|||
"""
|
||||
Function to be called when saving or changing a user's avatars.
|
||||
"""
|
||||
sizes = set(settings.AVATAR_AUTO_GENERATE_SIZES)
|
||||
sizes_key = get_cache_key(user, "cached_sizes")
|
||||
sizes = cache.get(sizes_key, set())
|
||||
if width is not None:
|
||||
sizes.add((width, height or width))
|
||||
for prefix in cached_funcs:
|
||||
for size in sizes:
|
||||
if isinstance(size, int):
|
||||
cache.delete(get_cache_key(user, size, size, prefix))
|
||||
cache.delete(get_cache_key(user, prefix, size))
|
||||
else:
|
||||
# Size is specified with height and width.
|
||||
cache.delete(get_cache_key(user, size[0], size[1], prefix))
|
||||
cache.delete(get_cache_key(user, prefix, size[0], size[1]))
|
||||
cache.set(sizes_key, set())
|
||||
|
||||
|
||||
def get_default_avatar_url():
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ from shutil import rmtree
|
|||
|
||||
from django.contrib.admin.sites import AdminSite
|
||||
from django.core import management
|
||||
from django.core.cache import cache
|
||||
from django.test import TestCase
|
||||
from django.test.utils import override_settings
|
||||
from django.urls import reverse
|
||||
|
|
@ -15,7 +16,12 @@ from avatar.conf import settings
|
|||
from avatar.models import Avatar
|
||||
from avatar.signals import avatar_deleted
|
||||
from avatar.templatetags import avatar_tags
|
||||
from avatar.utils import get_primary_avatar, get_user_model
|
||||
from avatar.utils import (
|
||||
get_cache_key,
|
||||
get_primary_avatar,
|
||||
get_user_model,
|
||||
invalidate_cache,
|
||||
)
|
||||
|
||||
|
||||
class AssertSignal:
|
||||
|
|
@ -465,10 +471,23 @@ class AvatarTests(TestCase):
|
|||
self.assertMediaFileExists(avatar_80_url)
|
||||
self.assertNotEqual(avatar_80_mtime, self.get_media_file_mtime(avatar_80_url))
|
||||
|
||||
# def testAvatarOrder
|
||||
# def testReplaceAvatarWhenMaxIsOne
|
||||
# def testHashFileName
|
||||
# def testHashUserName
|
||||
# def testChangePrimaryAvatar
|
||||
# def testDeleteThumbnailAndRecreation
|
||||
# def testAutomaticThumbnailCreation
|
||||
def test_invalidate_cache(self):
|
||||
upload_helper(self, "test.png")
|
||||
sizes_key = get_cache_key(self.user, "cached_sizes")
|
||||
sizes = cache.get(sizes_key, set())
|
||||
# Only default 80x80 thumbnail is cached
|
||||
self.assertEqual(len(sizes), 1)
|
||||
# Invalidate cache
|
||||
invalidate_cache(self.user)
|
||||
sizes = cache.get(sizes_key, set())
|
||||
# No thumbnail is cached.
|
||||
self.assertEqual(len(sizes), 0)
|
||||
# Create a custom 25x25 thumbnail and check that it is cached
|
||||
avatar_tags.avatar(self.user, 25)
|
||||
sizes = cache.get(sizes_key, set())
|
||||
self.assertEqual(len(sizes), 1)
|
||||
# Invalidate cache again.
|
||||
invalidate_cache(self.user)
|
||||
sizes = cache.get(sizes_key, set())
|
||||
# It should now be empty again
|
||||
self.assertEqual(len(sizes), 0)
|
||||
|
|
|
|||
Loading…
Reference in a new issue