Browse Source

Fixed #28601 -- Prevented cache.get_or_set() from caching None if default is a callable that returns None.

Daniel Tao 7 years ago
parent
commit
4d60261b2a
3 changed files with 14 additions and 6 deletions
  1. 7 5
      django/core/cache/backends/base.py
  2. 2 1
      docs/releases/1.11.7.txt
  3. 5 0
      tests/cache/tests.py

+ 7 - 5
django/core/cache/backends/base.py

@@ -155,13 +155,15 @@ class BaseCache:
         Return the value of the key stored or retrieved.
         """
         val = self.get(key, version=version)
-        if val is None and default is not None:
+        if val is None:
             if callable(default):
                 default = default()
-            self.add(key, default, timeout=timeout, version=version)
-            # Fetch the value again to avoid a race condition if another caller
-            # added a value between the first get() and the add() above.
-            return self.get(key, default, version=version)
+            if default is not None:
+                self.add(key, default, timeout=timeout, version=version)
+                # Fetch the value again to avoid a race condition if another
+                # caller added a value between the first get() and the add()
+                # above.
+                return self.get(key, default, version=version)
         return val
 
     def has_key(self, key, version=None):

+ 2 - 1
docs/releases/1.11.7.txt

@@ -9,4 +9,5 @@ Django 1.11.7 fixes several bugs in 1.11.6.
 Bugfixes
 ========
 
-* ...
+* Prevented ``cache.get_or_set()`` from caching ``None`` if the ``default``
+  argument is a callable that returns ``None`` (:ticket:`28601`).

+ 5 - 0
tests/cache/tests.py

@@ -924,6 +924,11 @@ class BaseCacheTests:
         self.assertEqual(cache.get_or_set('mykey', my_callable), 'value')
         self.assertEqual(cache.get_or_set('mykey', my_callable()), 'value')
 
+    def test_get_or_set_callable_returning_none(self):
+        self.assertIsNone(cache.get_or_set('mykey', lambda: None))
+        # Previous get_or_set() doesn't store None in the cache.
+        self.assertEqual(cache.get('mykey', 'default'), 'default')
+
     def test_get_or_set_version(self):
         msg = "get_or_set() missing 1 required positional argument: 'default'"
         cache.get_or_set('brian', 1979, version=2)