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

This commit is contained in:
Berker Peksag 2014-11-23 20:33:07 +02:00
parent a52cd407b8
commit 34fb909180
4 changed files with 64 additions and 1 deletions

View File

@ -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.

View File

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

View File

@ -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
tests/cache/tests.py vendored
View File

@ -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',