Author: russellm Date: 2010-01-27 02:21:35 -0600 (Wed, 27 Jan 2010) New Revision: 12306
Modified: django/trunk/AUTHORS django/trunk/django/core/cache/backends/base.py django/trunk/django/core/cache/backends/db.py django/trunk/django/core/cache/backends/dummy.py django/trunk/django/core/cache/backends/filebased.py django/trunk/django/core/cache/backends/locmem.py django/trunk/django/core/cache/backends/memcached.py django/trunk/docs/topics/cache.txt django/trunk/tests/regressiontests/cache/tests.py Log: Fixed #12671 -- Added set_many(), get_many(), and clear() methods to the cache backend interface. Thanks to Jeff Balogh for the report and patch. Modified: django/trunk/AUTHORS =================================================================== --- django/trunk/AUTHORS 2010-01-27 07:57:18 UTC (rev 12305) +++ django/trunk/AUTHORS 2010-01-27 08:21:35 UTC (rev 12306) @@ -56,6 +56,7 @@ Mike Axiak <ax...@mit.edu> Niran Babalola <ni...@niran.org> Morten Bagai <m...@bagai.com> + Jeff Balogh <jbal...@mozilla.com> Mikaƫl Barbero <mikael.barbero nospam at nospam free.fr> Randy Barlow <ra...@electronsweatshop.com> Scott Barr <sc...@divisionbyzero.com.au> Modified: django/trunk/django/core/cache/backends/base.py =================================================================== --- django/trunk/django/core/cache/backends/base.py 2010-01-27 07:57:18 UTC (rev 12305) +++ django/trunk/django/core/cache/backends/base.py 2010-01-27 08:21:35 UTC (rev 12306) @@ -91,3 +91,28 @@ # so that it always has the same functionality as has_key(), even # if a subclass overrides it. return self.has_key(key) + + def set_many(self, data, timeout=None): + """ + Set a bunch of values in the cache at once from a dict of key/value + pairs. For certain backends (memcached), this is much more efficient + than calling set() multiple times. + + If timeout is given, that timeout will be used for the key; otherwise + the default cache timeout will be used. + """ + for key, value in data.items(): + self.set(key, value, timeout) + + def delete_many(self, keys): + """ + Set a bunch of values in the cache at once. For certain backends + (memcached), this is much more efficient than calling delete() multiple + times. + """ + for key in keys: + self.delete(key) + + def clear(self): + """Remove *all* values from the cache at once.""" + raise NotImplementedError Modified: django/trunk/django/core/cache/backends/db.py =================================================================== --- django/trunk/django/core/cache/backends/db.py 2010-01-27 07:57:18 UTC (rev 12305) +++ django/trunk/django/core/cache/backends/db.py 2010-01-27 08:21:35 UTC (rev 12306) @@ -84,7 +84,7 @@ def _cull(self, cursor, now): if self._cull_frequency == 0: - cursor.execute("DELETE FROM %s" % self._table) + self.clear() else: cursor.execute("DELETE FROM %s WHERE expires < %%s" % self._table, [str(now)]) cursor.execute("SELECT COUNT(*) FROM %s" % self._table) @@ -92,3 +92,7 @@ if num > self._max_entries: cursor.execute("SELECT cache_key FROM %s ORDER BY cache_key LIMIT 1 OFFSET %%s" % self._table, [num / self._cull_frequency]) cursor.execute("DELETE FROM %s WHERE cache_key < %%s" % self._table, [cursor.fetchone()[0]]) + + def clear(self): + cursor = connection.cursor() + cursor.execute('DELETE FROM %s' % self._table) Modified: django/trunk/django/core/cache/backends/dummy.py =================================================================== --- django/trunk/django/core/cache/backends/dummy.py 2010-01-27 07:57:18 UTC (rev 12305) +++ django/trunk/django/core/cache/backends/dummy.py 2010-01-27 08:21:35 UTC (rev 12306) @@ -23,3 +23,12 @@ def has_key(self, *args, **kwargs): return False + + def set_many(self, *args, **kwargs): + pass + + def delete_many(self, *args, **kwargs): + pass + + def clear(self): + pass Modified: django/trunk/django/core/cache/backends/filebased.py =================================================================== --- django/trunk/django/core/cache/backends/filebased.py 2010-01-27 07:57:18 UTC (rev 12305) +++ django/trunk/django/core/cache/backends/filebased.py 2010-01-27 08:21:35 UTC (rev 12306) @@ -2,6 +2,7 @@ import os import time +import shutil try: import cPickle as pickle except ImportError: @@ -150,3 +151,9 @@ count += len(files) return count _num_entries = property(_get_num_entries) + + def clear(self): + try: + shutil.rmtree(self._dir) + except (IOError, OSError): + pass Modified: django/trunk/django/core/cache/backends/locmem.py =================================================================== --- django/trunk/django/core/cache/backends/locmem.py 2010-01-27 07:57:18 UTC (rev 12305) +++ django/trunk/django/core/cache/backends/locmem.py 2010-01-27 08:21:35 UTC (rev 12306) @@ -110,8 +110,7 @@ def _cull(self): if self._cull_frequency == 0: - self._cache.clear() - self._expire_info.clear() + self.clear() else: doomed = [k for (i, k) in enumerate(self._cache) if i % self._cull_frequency == 0] for k in doomed: @@ -133,3 +132,7 @@ self._delete(key) finally: self._lock.writer_leaves() + + def clear(self): + self._cache.clear() + self._expire_info.clear() Modified: django/trunk/django/core/cache/backends/memcached.py =================================================================== --- django/trunk/django/core/cache/backends/memcached.py 2010-01-27 07:57:18 UTC (rev 12305) +++ django/trunk/django/core/cache/backends/memcached.py 2010-01-27 08:21:35 UTC (rev 12306) @@ -71,3 +71,17 @@ if val is None: raise ValueError("Key '%s' not found" % key) return val + + def set_many(self, data, timeout=0): + safe_data = {} + for key, value in data.items(): + if isinstance(value, unicode): + value = value.encode('utf-8') + safe_data[smart_str(key)] = value + self._cache.set_multi(safe_data, timeout or self.default_timeout) + + def delete_many(self, keys): + self._cache.delete_multi(map(smart_str, keys)) + + def clear(self): + self._cache.flush_all() Modified: django/trunk/docs/topics/cache.txt =================================================================== --- django/trunk/docs/topics/cache.txt 2010-01-27 07:57:18 UTC (rev 12305) +++ django/trunk/docs/topics/cache.txt 2010-01-27 08:21:35 UTC (rev 12306) @@ -62,7 +62,7 @@ dramatically increase site performance. Memcached is available for free at http://danga.com/memcached/ . It runs as a -daemon and is allotted a specified amount of RAM. All it does is provide an +daemon and is allotted a specified amount of RAM. All it does is provide a fast interface for adding, retrieving and deleting arbitrary data in the cache. All data is stored directly in memory, so there's no overhead of database or filesystem usage. @@ -522,11 +522,37 @@ >>> cache.get_many(['a', 'b', 'c']) {'a': 1, 'b': 2, 'c': 3} -Finally, you can delete keys explicitly with ``delete()``. This is an easy way -of clearing the cache for a particular object:: +.. versionadded:: 1.2 +To set multiple values more efficiently, use ``set_many()`` to pass a dictionary +of key-value pairs:: + + >>> cache.set_many({'a': 1, 'b': 2, 'c': 3}) + >>> cache.get_many(['a', 'b', 'c']) + {'a': 1, 'b': 2, 'c': 3} + +Like ``cache.set()``, ``set_many()`` takes an optional ``timeout`` parameter. + +You can delete keys explicitly with ``delete()``. This is an easy way of +clearing the cache for a particular object:: + >>> cache.delete('a') +.. versionadded:: 1.2 + +If you want to clear a bunch of keys at once, ``delete_many()`` can take a list +of keys to be cleared:: + + >>> cache.delete_many(['a', 'b', 'c']) + +.. versionadded:: 1.2 + +Finally, if you want to delete all the keys in the cache, use +``cache.clear()``. Be careful with this; ``clear()`` will remove *everything* +from the cache, not just the keys set by your application. :: + + >>> cache.clear() + .. versionadded:: 1.1 You can also increment or decrement a key that already exists using the Modified: django/trunk/tests/regressiontests/cache/tests.py =================================================================== --- django/trunk/tests/regressiontests/cache/tests.py 2010-01-27 07:57:18 UTC (rev 12305) +++ django/trunk/tests/regressiontests/cache/tests.py 2010-01-27 08:21:35 UTC (rev 12306) @@ -129,9 +129,24 @@ self.cache.set(key, value) self.assertEqual(self.cache.get(key), None) + def test_set_many(self): + "set_many does nothing for the dummy cache backend" + self.cache.set_many({'a': 1, 'b': 2}) + def test_delete_many(self): + "delete_many does nothing for the dummy cache backend" + self.cache.delete_many(['a', 'b']) + + def test_clear(self): + "clear does nothing for the dummy cache backend" + self.cache.clear() + + class BaseCacheTests(object): # A common set of tests to apply to all cache backends + def tearDown(self): + self.cache.clear() + def test_simple(self): # Simple cache set/get works self.cache.set("key", "value") @@ -278,6 +293,37 @@ self.cache.set(key, value) self.assertEqual(self.cache.get(key), value) + def test_set_many(self): + # Multiple keys can be set using set_many + self.cache.set_many({"key1": "spam", "key2": "eggs"}) + self.assertEqual(self.cache.get("key1"), "spam") + self.assertEqual(self.cache.get("key2"), "eggs") + + def test_set_many_expiration(self): + # set_many takes a second ``timeout`` parameter + self.cache.set_many({"key1": "spam", "key2": "eggs"}, 1) + time.sleep(2) + self.assertEqual(self.cache.get("key1"), None) + self.assertEqual(self.cache.get("key2"), None) + + def test_delete_many(self): + # Multiple keys can be deleted using delete_many + self.cache.set("key1", "spam") + self.cache.set("key2", "eggs") + self.cache.set("key3", "ham") + self.cache.delete_many(["key1", "key2"]) + self.assertEqual(self.cache.get("key1"), None) + self.assertEqual(self.cache.get("key2"), None) + self.assertEqual(self.cache.get("key3"), "ham") + + def test_clear(self): + # The cache can be emptied using clear + self.cache.set("key1", "spam") + self.cache.set("key2", "eggs") + self.cache.clear() + self.assertEqual(self.cache.get("key1"), None) + self.assertEqual(self.cache.get("key2"), None) + class DBCacheTests(unittest.TestCase, BaseCacheTests): def setUp(self): management.call_command('createcachetable', 'test_cache_table', verbosity=0, interactive=False) @@ -286,7 +332,7 @@ def tearDown(self): from django.db import connection cursor = connection.cursor() - cursor.execute('DROP TABLE test_cache_table'); + cursor.execute('DROP TABLE test_cache_table') class LocMemCacheTests(unittest.TestCase, BaseCacheTests): def setUp(self): @@ -309,9 +355,6 @@ self.dirname = tempfile.mkdtemp() self.cache = get_cache('file://%s' % self.dirname) - def tearDown(self): - shutil.rmtree(self.dirname) - def test_hashing(self): """Test that keys are hashed into subdirectories correctly""" self.cache.set("foo", "bar") -- You received this message because you are subscribed to the Google Groups "Django updates" group. To post to this group, send email to django-upda...@googlegroups.com. To unsubscribe from this group, send email to django-updates+unsubscr...@googlegroups.com. For more options, visit this group at http://groups.google.com/group/django-updates?hl=en.