Commit 34fb9091 authored by Berker Peksag's avatar Berker Peksag
Browse files

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

parent a52cd407
Loading
Loading
Loading
Loading
+21 −0
Original line number Diff line number Diff line
@@ -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
Original line number Diff line number Diff line
@@ -94,7 +94,8 @@ Minor features
Cache
^^^^^

* ...
* ``django.core.cache.backends.base.BaseCache`` now has a ``get_or_set()``
  method.

Email
^^^^^
+19 −0
Original line number Diff line number Diff line
@@ -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
Original line number Diff line number Diff line
@@ -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',