Ticket #13795: cache_key_prefix_13448.diff
File cache_key_prefix_13448.diff, 17.2 KB (added by , 14 years ago) |
---|
-
django/conf/global_settings.py
427 427 # The cache backend to use. See the docstring in django.core.cache for the 428 428 # possible values. 429 429 CACHE_BACKEND = 'locmem://' 430 CACHE_KEY_PREFIX = '' 430 431 CACHE_MIDDLEWARE_KEY_PREFIX = '' 431 432 CACHE_MIDDLEWARE_SECONDS = 600 432 433 -
django/core/cache/__init__.py
56 56 57 57 return scheme, host, params 58 58 59 def get_cache(backend_uri ):59 def get_cache(backend_uri, key_prefix=settings.CACHE_KEY_PREFIX): 60 60 scheme, host, params = parse_backend_uri(backend_uri) 61 61 if scheme in BACKENDS: 62 62 name = 'django.core.cache.backends.%s' % BACKENDS[scheme] 63 63 else: 64 64 name = scheme 65 65 module = importlib.import_module(name) 66 return getattr(module, 'CacheClass')(host, params )66 return getattr(module, 'CacheClass')(host, params, key_prefix) 67 67 68 68 cache = get_cache(settings.CACHE_BACKEND) 69 69 70 # Some caches -- python t-memcached in particular -- need to do a cleanup at the70 # Some caches -- python-memcached in particular -- need to do a cleanup at the 71 71 # end of a request cycle. If the cache provides a close() method, wire it up 72 72 # here. 73 73 if hasattr(cache, 'close'): -
django/core/cache/backends/base.py
1 1 "Base Cache class." 2 2 3 3 from django.core.exceptions import ImproperlyConfigured 4 from django.utils.encoding import smart_str 4 5 5 6 class InvalidCacheBackendError(ImproperlyConfigured): 6 7 pass 7 8 8 9 class BaseCache(object): 9 def __init__(self, params ):10 def __init__(self, params, key_prefix=''): 10 11 timeout = params.get('timeout', 300) 11 12 try: 12 13 timeout = int(timeout) 13 14 except (ValueError, TypeError): 14 15 timeout = 300 15 16 self.default_timeout = timeout 17 self.key_prefix = smart_str(key_prefix) 16 18 19 def make_key(self, key): 20 """Constructs the key used by all other methods. By default it prepends 21 the `key_prefix'. In cache backend subclasses this can be overriden to 22 provide custom key making behavior. 23 """ 24 return self.key_prefix + smart_str(key) 25 17 26 def add(self, key, value, timeout=None): 18 27 """ 19 28 Set a value in the cache if the key does not already exist. If -
django/core/cache/backends/locmem.py
10 10 from django.utils.synch import RWLock 11 11 12 12 class CacheClass(BaseCache): 13 def __init__(self, _, params ):14 BaseCache.__init__(self, params )13 def __init__(self, _, params, key_prefix): 14 BaseCache.__init__(self, params, key_prefix='') 15 15 self._cache = {} 16 16 self._expire_info = {} 17 17 … … 30 30 self._lock = RWLock() 31 31 32 32 def add(self, key, value, timeout=None): 33 key = self.make_key(key) 33 34 self._lock.writer_enters() 34 35 try: 35 36 exp = self._expire_info.get(key) … … 44 45 self._lock.writer_leaves() 45 46 46 47 def get(self, key, default=None): 48 key = self.make_key(key) 47 49 self._lock.reader_enters() 48 50 try: 49 51 exp = self._expire_info.get(key) … … 76 78 self._expire_info[key] = time.time() + timeout 77 79 78 80 def set(self, key, value, timeout=None): 81 key = self.make_key(key) 79 82 self._lock.writer_enters() 80 83 # Python 2.4 doesn't allow combined try-except-finally blocks. 81 84 try: … … 87 90 self._lock.writer_leaves() 88 91 89 92 def has_key(self, key): 93 key = self.make_key(key) 90 94 self._lock.reader_enters() 91 95 try: 92 96 exp = self._expire_info.get(key) … … 127 131 pass 128 132 129 133 def delete(self, key): 134 key = self.make_key(key) 130 135 self._lock.writer_enters() 131 136 try: 132 137 self._delete(key) -
django/core/cache/backends/filebased.py
12 12 from django.utils.hashcompat import md5_constructor 13 13 14 14 class CacheClass(BaseCache): 15 def __init__(self, dir, params ):16 BaseCache.__init__(self, params )15 def __init__(self, dir, params, key_prefix=''): 16 BaseCache.__init__(self, params, key_prefix) 17 17 18 18 max_entries = params.get('max_entries', 300) 19 19 try: … … 32 32 self._createdir() 33 33 34 34 def add(self, key, value, timeout=None): 35 key = self.make_key(key) 35 36 if self.has_key(key): 36 37 return False 37 38 … … 39 40 return True 40 41 41 42 def get(self, key, default=None): 43 key = self.make_key(key) 42 44 fname = self._key_to_file(key) 43 45 try: 44 46 f = open(fname, 'rb') … … 56 58 return default 57 59 58 60 def set(self, key, value, timeout=None): 61 key = self.make_key(key) 59 62 fname = self._key_to_file(key) 60 63 dirname = os.path.dirname(fname) 61 64 … … 79 82 pass 80 83 81 84 def delete(self, key): 85 key = self.make_key(key) 82 86 try: 83 87 self._delete(self._key_to_file(key)) 84 88 except (IOError, OSError): … … 95 99 pass 96 100 97 101 def has_key(self, key): 102 key = self.make_key(key) 98 103 fname = self._key_to_file(key) 99 104 try: 100 105 f = open(fname, 'rb') … … 148 153 Thus, a cache key of "foo" gets turnned into a file named 149 154 ``{cache-dir}ac/bd/18db4cc2f85cedef654fccc4a4d8``. 150 155 """ 151 path = md5_constructor(key.encode('utf-8')).hexdigest() 156 key = self.make_key(key) 157 path = md5_constructor(key).hexdigest() 152 158 path = os.path.join(path[:2], path[2:4], path[4:]) 153 159 return os.path.join(self._dir, path) 154 160 -
django/core/cache/backends/db.py
10 10 import pickle 11 11 12 12 class CacheClass(BaseCache): 13 def __init__(self, table, params ):14 BaseCache.__init__(self, params )13 def __init__(self, table, params, key_prefix=''): 14 BaseCache.__init__(self, params, key_prefix) 15 15 self._table = connection.ops.quote_name(table) 16 16 max_entries = params.get('max_entries', 300) 17 17 try: … … 25 25 self._cull_frequency = 3 26 26 27 27 def get(self, key, default=None): 28 key = self.make_key(key) 28 29 cursor = connection.cursor() 29 30 cursor.execute("SELECT cache_key, value, expires FROM %s WHERE cache_key = %%s" % self._table, [key]) 30 31 row = cursor.fetchone() … … 45 46 return self._base_set('add', key, value, timeout) 46 47 47 48 def _base_set(self, mode, key, value, timeout=None): 49 key = self.make_key(key) 48 50 if timeout is None: 49 51 timeout = self.default_timeout 50 52 cursor = connection.cursor() … … 74 76 return True 75 77 76 78 def delete(self, key): 79 key = self.make_key(key) 77 80 cursor = connection.cursor() 78 81 cursor.execute("DELETE FROM %s WHERE cache_key = %%s" % self._table, [key]) 79 82 transaction.commit_unless_managed() 80 83 81 84 def has_key(self, key): 85 key = self.make_key(key) 82 86 now = datetime.now().replace(microsecond=0) 83 87 cursor = connection.cursor() 84 88 cursor.execute("SELECT cache_key FROM %s WHERE cache_key = %%s and expires > %%s" % self._table, -
django/core/cache/backends/memcached.py
3 3 import time 4 4 5 5 from django.core.cache.backends.base import BaseCache, InvalidCacheBackendError 6 from django.utils.encoding import smart_unicode, smart_str7 6 8 7 try: 9 8 import cmemcache as memcache … … 19 18 raise InvalidCacheBackendError("Memcached cache backend requires either the 'memcache' or 'cmemcache' library") 20 19 21 20 class CacheClass(BaseCache): 22 def __init__(self, server, params ):23 BaseCache.__init__(self, params )21 def __init__(self, server, params, key_prefix=''): 22 BaseCache.__init__(self, params, key_prefix) 24 23 self._cache = memcache.Client(server.split(';')) 25 24 26 25 def _get_memcache_timeout(self, timeout): … … 40 39 return timeout 41 40 42 41 def add(self, key, value, timeout=0): 42 key = self.make_key(key) 43 43 if isinstance(value, unicode): 44 44 value = value.encode('utf-8') 45 return self._cache.add( smart_str(key), value, self._get_memcache_timeout(timeout))45 return self._cache.add(key, value, self._get_memcache_timeout(timeout)) 46 46 47 47 def get(self, key, default=None): 48 val = self._cache.get(smart_str(key)) 48 key = self.make_key(key) 49 val = self._cache.get(key) 49 50 if val is None: 50 51 return default 51 52 return val 52 53 53 54 def set(self, key, value, timeout=0): 54 self._cache.set(smart_str(key), value, self._get_memcache_timeout(timeout)) 55 key = self.make_key(key) 56 self._cache.set(key, value, self._get_memcache_timeout(timeout)) 55 57 56 58 def delete(self, key): 57 self._cache.delete(smart_str(key)) 59 key = self.make_key(key) 60 self._cache.delete(key) 58 61 59 62 def get_many(self, keys): 60 return self._cache.get_multi(map(smart_str,keys)) 63 keys = map(self.make_key, keys) 64 return self._cache.get_multi(keys) 61 65 62 66 def close(self, **kwargs): 63 67 self._cache.disconnect_all() 64 68 65 69 def incr(self, key, delta=1): 70 key = self.make_key(key) 66 71 try: 67 72 val = self._cache.incr(key, delta) 68 73 … … 77 82 return val 78 83 79 84 def decr(self, key, delta=1): 85 key = self.make_key(key) 80 86 try: 81 87 val = self._cache.decr(key, delta) 82 88 … … 92 98 def set_many(self, data, timeout=0): 93 99 safe_data = {} 94 100 for key, value in data.items(): 101 key = self.make_key(key) 95 102 if isinstance(value, unicode): 96 103 value = value.encode('utf-8') 97 safe_data[ smart_str(key)] = value104 safe_data[key] = value 98 105 self._cache.set_multi(safe_data, self._get_memcache_timeout(timeout)) 99 106 100 107 def delete_many(self, keys): 101 self._cache.delete_multi(map(smart_str, keys)) 108 keys = map(self.make_key, keys) 109 self._cache.delete_multi(keys) 102 110 103 111 def clear(self): 104 112 self._cache.flush_all() -
tests/regressiontests/cache/tests.py
143 143 "clear does nothing for the dummy cache backend" 144 144 self.cache.clear() 145 145 146 147 146 class BaseCacheTests(object): 148 147 # A common set of tests to apply to all cache backends 149 148 def tearDown(self): … … 161 160 self.assertEqual(result, False) 162 161 self.assertEqual(self.cache.get("addkey1"), "value") 163 162 163 def test_prefix(self): 164 # Test for same cache key conflicts between shared backend 165 self.cache.set('somekey', 'value') 166 167 # should not be set in the prefixed cache 168 self.assertFalse(self.pfx_cache.has_key('somekey')) 169 170 self.pfx_cache.set('somekey', 'value2') 171 172 self.assertEqual(self.cache.get('somekey'), 'value') 173 self.assertEqual(self.pfx_cache.get('somekey'), 'value2') 174 164 175 def test_non_existent(self): 165 176 # Non-existent cache keys return as None/default 166 177 # get with non-existent keys … … 358 369 self._table_name = 'test cache table' 359 370 management.call_command('createcachetable', self._table_name, verbosity=0, interactive=False) 360 371 self.cache = get_cache('db://%s' % self._table_name) 372 self.pfx_cache = get_cache('db://%s' % self._table_name, 'cacheprefix') 361 373 362 374 def tearDown(self): 363 375 from django.db import connection … … 367 379 class LocMemCacheTests(unittest.TestCase, BaseCacheTests): 368 380 def setUp(self): 369 381 self.cache = get_cache('locmem://') 382 self.pfx_cache = get_cache('locmem://', 'cacheprefix') 370 383 371 384 # memcached backend isn't guaranteed to be available. 372 385 # To check the memcached backend, the test settings file will … … 376 389 class MemcachedCacheTests(unittest.TestCase, BaseCacheTests): 377 390 def setUp(self): 378 391 self.cache = get_cache(settings.CACHE_BACKEND) 392 self.pfx_cache = get_cache(settings.CACHE_BACKEND, 'cacheprefix') 379 393 380 394 class FileBasedCacheTests(unittest.TestCase, BaseCacheTests): 381 395 """ … … 384 398 def setUp(self): 385 399 self.dirname = tempfile.mkdtemp() 386 400 self.cache = get_cache('file://%s' % self.dirname) 401 self.pfx_cache = get_cache('file://%s' % self.dirname, 'cacheprefix') 387 402 388 403 def test_hashing(self): 389 404 """Test that keys are hashed into subdirectories correctly""" … … 411 426 412 427 def setUp(self): 413 428 self.path = '/cache/test/' 414 self.old_ settings_key_prefix = settings.CACHE_MIDDLEWARE_KEY_PREFIX415 self.old_ middleware_seconds = settings.CACHE_MIDDLEWARE_SECONDS429 self.old_cache_middleware_key_prefix = settings.CACHE_MIDDLEWARE_KEY_PREFIX 430 self.old_cache_middleware_seconds = settings.CACHE_MIDDLEWARE_SECONDS 416 431 self.orig_use_i18n = settings.USE_I18N 417 432 settings.CACHE_MIDDLEWARE_KEY_PREFIX = 'settingsprefix' 418 433 settings.CACHE_MIDDLEWARE_SECONDS = 1 419 434 settings.USE_I18N = False 420 435 421 436 def tearDown(self): 422 settings.CACHE_MIDDLEWARE_KEY_PREFIX = self.old_ settings_key_prefix423 settings.CACHE_MIDDLEWARE_SECONDS = self.old_ middleware_seconds437 settings.CACHE_MIDDLEWARE_KEY_PREFIX = self.old_cache_middleware_key_prefix 438 settings.CACHE_MIDDLEWARE_SECONDS = self.old_cache_middleware_seconds 424 439 settings.USE_I18N = self.orig_use_i18n 425 440 426 441 def _get_request(self, path): … … 473 488 learn_cache_key(request, response) 474 489 self.assertEqual(get_cache_key(request), 'views.decorators.cache.cache_page.settingsprefix.a8c87a3d8c44853d7f79474f7ffe4ad5.d41d8cd98f00b204e9800998ecf8427e') 475 490 491 class PrefixedCacheUtils(CacheUtils): 492 def setUp(self): 493 super(PrefixedCacheUtils, self).setUp() 494 self.old_cache_key_prefix = settings.CACHE_KEY_PREFIX 495 settings.CACHE_KEY_PREFIX = 'cacheprefix' 496 497 def tearDown(self): 498 super(PrefixedCacheUtils, self).tearDown() 499 settings.CACHE_KEY_PREFIX = self.old_cache_key_prefix 500 476 501 class CacheI18nTest(unittest.TestCase): 477 502 478 503 def setUp(self): … … 568 593 get_cache_data = FetchFromCacheMiddleware().process_request(request) 569 594 self.assertEqual(get_cache_data.content, es_message) 570 595 596 class PrefixedCacheI18nTest(CacheI18nTest): 597 def setUp(self): 598 super(PrefixedCacheI18nTest, self).setUp() 599 self.old_cache_key_prefix = settings.CACHE_KEY_PREFIX 600 settings.CACHE_KEY_PREFIX = 'cacheprefix' 601 602 def tearDown(self): 603 super(PrefixedCacheI18nTest, self).tearDown() 604 settings.CACHE_KEY_PREFIX = self.old_cache_key_prefix 605 571 606 if __name__ == '__main__': 572 607 unittest.main() -
docs/topics/cache.txt
600 600 However, if the backend doesn't natively provide an increment/decrement 601 601 operation, it will be implemented using a two-step retrieve/update. 602 602 603 CACHE_KEY_PREFIX 604 ---------------- 605 606 It is a common occurence to have a shared cache instance running on your development 607 or production server for use across multiple projects, i.e. all sites are pointing 608 to the memcached instance running on port 11211. As a result, cache key conflicts 609 may arise which could result in data from one site being used by another. 610 611 To alleviate this, CACHE_KEY_PREFIX can be set. This will be prepended to all keys 612 that are used with the cache backend, transparently:: 613 614 # in settings, CACHE_KEY_PREFIX = 'myproject_' 615 616 >>> cache.set('my_key', 'hello world!') # set with key 'myproject_my_key' 617 >>> cache.get('my_key') # retrieved with key 'myproject_my_key' 618 'hello world!' 619 620 Of course, for sites that _do_ share content, simply set the CACHE_KEY_PREFIX for 621 both sites to the same value. The default value for CACHE_KEY_PREFIX is the empty 622 string ``''``. 623 624 .. note:: 625 626 This does *not* conflict with the CACHE_MIDDLEWARE_KEY_PREFIX and can be used 627 in conjunction with it. CACHE_KEY_PREFIX acts as a global prefix for a 628 particular cache instance, therefore it will be prepended to the 629 CACHE_MIDDLEWARE_KEY_PREFIX transparently. 630 603 631 Upstream caches 604 632 =============== 605 633