[4.0.x] Fixed #33361 -- Fixed Redis cache backend crash on booleans.

Backport of 2f33217ea2 from main
This commit is contained in:
Jeremy Lainé 2021-12-13 17:09:13 +01:00 committed by Mariusz Felisiak
parent cc5bbd447b
commit 3b03bce122
3 changed files with 21 additions and 4 deletions

View File

@ -10,8 +10,14 @@ from django.utils.module_loading import import_string
class RedisSerializer(PickleSerializer): class RedisSerializer(PickleSerializer):
"""
Similar to PickSerializer, except integers are serialized as native Redis
integers for better incr() and decr() atomicity.
"""
def dumps(self, obj): def dumps(self, obj):
if isinstance(obj, int): # Only skip pickling for integers, a int subclasses as bool should be
# pickled.
if type(obj) is int:
return obj return obj
return super().dumps(obj) return super().dumps(obj)

View File

@ -12,3 +12,6 @@ Bugfixes
* Fixed a regression in Django 4.0 that caused a crash of * Fixed a regression in Django 4.0 that caused a crash of
:meth:`~django.test.SimpleTestCase.assertFormsetError` on a formset named :meth:`~django.test.SimpleTestCase.assertFormsetError` on a formset named
``form`` (:ticket:`33346`). ``form`` (:ticket:`33346`).
* Fixed a bug in Django 4.0 that caused a crash on booleans with the
``RedisCache`` backend (:ticket:`33361`).

14
tests/cache/tests.py vendored
View File

@ -411,17 +411,20 @@ class BaseCacheTests:
def test_data_types(self): def test_data_types(self):
# Many different data types can be cached # Many different data types can be cached
stuff = { tests = {
'string': 'this is a string', 'string': 'this is a string',
'int': 42, 'int': 42,
'bool': True,
'list': [1, 2, 3, 4], 'list': [1, 2, 3, 4],
'tuple': (1, 2, 3, 4), 'tuple': (1, 2, 3, 4),
'dict': {'A': 1, 'B': 2}, 'dict': {'A': 1, 'B': 2},
'function': f, 'function': f,
'class': C, 'class': C,
} }
cache.set("stuff", stuff) for key, value in tests.items():
self.assertEqual(cache.get("stuff"), stuff) with self.subTest(key=key):
cache.set(key, value)
self.assertEqual(cache.get(key), value)
def test_cache_read_for_model_instance(self): def test_cache_read_for_model_instance(self):
# Don't want fields with callable as default to be called on cache read # Don't want fields with callable as default to be called on cache read
@ -1785,6 +1788,11 @@ class RedisCacheTests(BaseCacheTests, TestCase):
def test_get_client(self): def test_get_client(self):
self.assertIsInstance(cache._cache.get_client(), self.lib.Redis) self.assertIsInstance(cache._cache.get_client(), self.lib.Redis)
def test_serializer_dumps(self):
self.assertEqual(cache._cache._serializer.dumps(123), 123)
self.assertIsInstance(cache._cache._serializer.dumps(True), bytes)
self.assertIsInstance(cache._cache._serializer.dumps('abc'), bytes)
class FileBasedCachePathLibTests(FileBasedCacheTests): class FileBasedCachePathLibTests(FileBasedCacheTests):
def mkdtemp(self): def mkdtemp(self):