Parcourir la source

Fixed #12982 -- Added a get_or_set() method to the BaseCache backend.

Berker Peksag il y a 10 ans
Parent
commit
34fb909180
4 fichiers modifiés avec 64 ajouts et 1 suppressions
  1. 21 0
      django/core/cache/backends/base.py
  2. 2 1
      docs/releases/1.9.txt
  3. 19 0
      docs/topics/cache.txt
  4. 22 0
      tests/cache/tests.py

+ 21 - 0
django/core/cache/backends/base.py

@@ -147,6 +147,27 @@ class BaseCache(object):
                 d[k] = val
         return d
 
+    def get_or_set(self, key, default=None, timeout=DEFAULT_TIMEOUT, version=None):
+        """
+        Fetch a given key from the cache. If the key does not exist,
+        the key is added and set to the default value. The default value can
+        also be any callable. If timeout is given, that timeout will be used
+        for the key; otherwise the default cache timeout will be used.
+
+        Returns the value of the key stored or retrieved on success,
+        False on error.
+        """
+        if default is None:
+            raise ValueError('You need to specify a value.')
+        val = self.get(key, version=version)
+        if val is None:
+            if callable(default):
+                default = default()
+            val = self.add(key, default, timeout=timeout, version=version)
+            if val:
+                return self.get(key, version=version)
+        return val
+
     def has_key(self, key, version=None):
         """
         Returns True if the key is in the cache and has not expired.

+ 2 - 1
docs/releases/1.9.txt

@@ -94,7 +94,8 @@ Minor features
 Cache
 ^^^^^
 
-* ...
+* ``django.core.cache.backends.base.BaseCache`` now has a ``get_or_set()``
+  method.
 
 Email
 ^^^^^

+ 19 - 0
docs/topics/cache.txt

@@ -778,6 +778,25 @@ If you need to know whether ``add()`` stored a value in the cache, you can
 check the return value. It will return ``True`` if the value was stored,
 ``False`` otherwise.
 
+If you want to get a key's value or set a value if the key isn't in the cache,
+there is the ``get_or_set()`` method. It takes the same parameters as ``get()``
+but the default is set as the new cache value for that key, rather than simply
+returned::
+
+    >>> cache.get('my_new_key')  # returns None
+    >>> cache.get_or_set('my_new_key', 'my new value', 100)
+    'my new value'
+
+You can also pass any callable as a *default* value::
+
+    >>> import datetime
+    >>> cache.get_or_set('some-timestamp-key', datetime.datetime.now)
+    datetime.datetime(2014, 12, 11, 0, 15, 49, 457920)
+
+.. versionchanged:: 1.9
+
+    The ``get_or_set()`` method was added.
+
 There's also a ``get_many()`` interface that only hits the cache once.
 ``get_many()`` returns a dictionary with all the keys you asked for that
 actually exist in the cache (and haven't expired)::

+ 22 - 0
tests/cache/tests.py

@@ -884,6 +884,28 @@ class BaseCacheTests(object):
         with self.assertRaises(pickle.PickleError):
             cache.set('unpickable', Unpickable())
 
+    def test_get_or_set(self):
+        self.assertIsNone(cache.get('projector'))
+        self.assertEqual(cache.get_or_set('projector', 42), 42)
+        self.assertEqual(cache.get('projector'), 42)
+
+    def test_get_or_set_callable(self):
+        def my_callable():
+            return 'value'
+
+        self.assertEqual(cache.get_or_set('mykey', my_callable), 'value')
+
+    def test_get_or_set_version(self):
+        cache.get_or_set('brian', 1979, version=2)
+        with self.assertRaisesMessage(ValueError, 'You need to specify a value.'):
+            cache.get_or_set('brian')
+        with self.assertRaisesMessage(ValueError, 'You need to specify a value.'):
+            cache.get_or_set('brian', version=1)
+        self.assertIsNone(cache.get('brian', version=1))
+        self.assertEqual(cache.get_or_set('brian', 42, version=1), 42)
+        self.assertEqual(cache.get_or_set('brian', 1979, version=2), 1979)
+        self.assertIsNone(cache.get('brian', version=3))
+
 
 @override_settings(CACHES=caches_setting_for_tests(
     BACKEND='django.core.cache.backends.db.DatabaseCache',