summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBerker Peksag <berker.peksag@gmail.com>2014-11-23 20:33:07 +0200
committerBerker Peksag <berker.peksag@gmail.com>2015-03-14 20:07:16 +0200
commit34fb909180e9df06fa6a993dd5696a49cd152a0b (patch)
tree58dc89a71e6579ebfcb4abed5fc7a8c0c2b0b023
parenta52cd407b86a51e1badf6771e590361e24fd7155 (diff)
Fixed #12982 -- Added a get_or_set() method to the BaseCache backend.
-rw-r--r--django/core/cache/backends/base.py21
-rw-r--r--docs/releases/1.9.txt3
-rw-r--r--docs/topics/cache.txt19
-rw-r--r--tests/cache/tests.py22
4 files changed, 64 insertions, 1 deletions
diff --git a/django/core/cache/backends/base.py b/django/core/cache/backends/base.py
index 7ac1e5bc32..26113aaa31 100644
--- a/django/core/cache/backends/base.py
+++ b/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.
diff --git a/docs/releases/1.9.txt b/docs/releases/1.9.txt
index e8dd955e29..1be30ae561 100644
--- a/docs/releases/1.9.txt
+++ b/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
^^^^^
diff --git a/docs/topics/cache.txt b/docs/topics/cache.txt
index 94a97d5b0e..69656534e6 100644
--- a/docs/topics/cache.txt
+++ b/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)::
diff --git a/tests/cache/tests.py b/tests/cache/tests.py
index 168a808306..ffbed67197 100644
--- a/tests/cache/tests.py
+++ b/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',