Ticket #13795: cache_key_prefix_13891.diff
File cache_key_prefix_13891.diff, 18.1 KB (added by , 14 years ago) |
---|
-
django/conf/global_settings.py
428 428 # The cache backend to use. See the docstring in django.core.cache for the 429 429 # possible values. 430 430 CACHE_BACKEND = 'locmem://' 431 CACHE_KEY_PREFIX = '' 431 432 CACHE_MIDDLEWARE_KEY_PREFIX = '' 432 433 CACHE_MIDDLEWARE_SECONDS = 600 433 434 -
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=''): 60 if not key_prefix: 61 key_prefix = settings.CACHE_KEY_PREFIX 60 62 scheme, host, params = parse_backend_uri(backend_uri) 61 63 if scheme in BACKENDS: 62 64 name = 'django.core.cache.backends.%s' % BACKENDS[scheme] 63 65 else: 64 66 name = scheme 65 67 module = importlib.import_module(name) 66 return module.CacheClass(host, params )68 return module.CacheClass(host, params, key_prefix) 67 69 68 70 cache = get_cache(settings.CACHE_BACKEND) 69 71 70 # Some caches -- python t-memcached in particular -- need to do a cleanup at the72 # Some caches -- python-memcached in particular -- need to do a cleanup at the 71 73 # end of a request cycle. If the cache provides a close() method, wire it up 72 74 # here. 73 75 if hasattr(cache, 'close'): -
django/core/cache/backends/base.py
3 3 import warnings 4 4 5 5 from django.core.exceptions import ImproperlyConfigured, DjangoRuntimeWarning 6 from django.utils.encoding import smart_str 6 7 7 8 class InvalidCacheBackendError(ImproperlyConfigured): 8 9 pass … … 14 15 MEMCACHE_MAX_KEY_LENGTH = 250 15 16 16 17 class BaseCache(object): 17 def __init__(self, params ):18 def __init__(self, params, key_prefix=''): 18 19 timeout = params.get('timeout', 300) 19 20 try: 20 21 timeout = int(timeout) 21 22 except (ValueError, TypeError): 22 23 timeout = 300 23 24 self.default_timeout = timeout 25 self.key_prefix = smart_str(key_prefix) 24 26 27 def make_key(self, key): 28 """Constructs the key used by all other methods. By default it prepends 29 the `key_prefix'. In cache backend subclasses this can be overriden to 30 provide custom key making behavior. 31 """ 32 return self.key_prefix + smart_str(key) 33 25 34 def add(self, key, value, timeout=None): 26 35 """ 27 36 Set a value in the cache if the key does not already exist. If -
django/core/cache/backends/db.py
26 26 self.proxy = False 27 27 28 28 class CacheClass(BaseCache): 29 def __init__(self, table, params ):30 BaseCache.__init__(self, params )29 def __init__(self, table, params, key_prefix=''): 30 BaseCache.__init__(self, params, key_prefix) 31 31 self._table = table 32 32 33 33 class CacheEntry(object): … … 46 46 self._cull_frequency = 3 47 47 48 48 def get(self, key, default=None): 49 key = self.make_key(key) 49 50 self.validate_key(key) 50 51 db = router.db_for_read(self.cache_model_class) 51 52 table = connections[db].ops.quote_name(self._table) … … 66 67 return pickle.loads(base64.decodestring(value)) 67 68 68 69 def set(self, key, value, timeout=None): 70 key = self.make_key(key) 69 71 self.validate_key(key) 70 72 self._base_set('set', key, value, timeout) 71 73 72 74 def add(self, key, value, timeout=None): 75 key = self.make_key(key) 73 76 self.validate_key(key) 74 77 return self._base_set('add', key, value, timeout) 75 78 … … 106 109 return True 107 110 108 111 def delete(self, key): 112 key = self.make_key(key) 109 113 self.validate_key(key) 110 114 db = router.db_for_write(self.cache_model_class) 111 115 table = connections[db].ops.quote_name(self._table) … … 115 119 transaction.commit_unless_managed(using=db) 116 120 117 121 def has_key(self, key): 122 key = self.make_key(key) 118 123 self.validate_key(key) 119 124 db = router.db_for_read(self.cache_model_class) 120 125 table = connections[db].ops.quote_name(self._table) -
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 self.validate_key(key) 35 pfx_key = self.make_key(key) 36 self.validate_key(pfx_key) 36 37 if self.has_key(key): 37 38 return False 38 39 … … 40 41 return True 41 42 42 43 def get(self, key, default=None): 44 key = self.make_key(key) 43 45 self.validate_key(key) 44 46 fname = self._key_to_file(key) 45 47 try: … … 58 60 return default 59 61 60 62 def set(self, key, value, timeout=None): 63 key = self.make_key(key) 61 64 self.validate_key(key) 62 65 fname = self._key_to_file(key) 63 66 dirname = os.path.dirname(fname) … … 82 85 pass 83 86 84 87 def delete(self, key): 88 key = self.make_key(key) 85 89 self.validate_key(key) 86 90 try: 87 91 self._delete(self._key_to_file(key)) … … 99 103 pass 100 104 101 105 def has_key(self, key): 106 key = self.make_key(key) 102 107 self.validate_key(key) 103 108 fname = self._key_to_file(key) 104 109 try: … … 153 158 Thus, a cache key of "foo" gets turnned into a file named 154 159 ``{cache-dir}ac/bd/18db4cc2f85cedef654fccc4a4d8``. 155 160 """ 156 path = md5_constructor(key .encode('utf-8')).hexdigest()161 path = md5_constructor(key).hexdigest() 157 162 path = os.path.join(path[:2], path[2:4], path[4:]) 158 163 return os.path.join(self._dir, path) 159 164 -
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.validate_key(key) 34 35 self._lock.writer_enters() 35 36 try: … … 45 46 self._lock.writer_leaves() 46 47 47 48 def get(self, key, default=None): 49 key = self.make_key(key) 48 50 self.validate_key(key) 49 51 self._lock.reader_enters() 50 52 try: … … 78 80 self._expire_info[key] = time.time() + timeout 79 81 80 82 def set(self, key, value, timeout=None): 83 key = self.make_key(key) 81 84 self.validate_key(key) 82 85 self._lock.writer_enters() 83 86 # Python 2.4 doesn't allow combined try-except-finally blocks. … … 90 93 self._lock.writer_leaves() 91 94 92 95 def has_key(self, key): 96 key = self.make_key(key) 93 97 self.validate_key(key) 94 98 self._lock.reader_enters() 95 99 try: … … 131 135 pass 132 136 133 137 def delete(self, key): 138 key = self.make_key(key) 134 139 self.validate_key(key) 135 140 self._lock.writer_enters() 136 141 try: -
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(s mart_str,keys))63 return self._cache.get_multi(map(self.make_key,keys)) 61 64 62 65 def close(self, **kwargs): 63 66 self._cache.disconnect_all() 64 67 65 68 def incr(self, key, delta=1): 69 key = self.make_key(key) 66 70 try: 67 71 val = self._cache.incr(key, delta) 68 72 … … 77 81 return val 78 82 79 83 def decr(self, key, delta=1): 84 key = self.make_key(key) 80 85 try: 81 86 val = self._cache.decr(key, delta) 82 87 … … 92 97 def set_many(self, data, timeout=0): 93 98 safe_data = {} 94 99 for key, value in data.items(): 100 key = self.make_key(key) 95 101 if isinstance(value, unicode): 96 102 value = value.encode('utf-8') 97 safe_data[ smart_str(key)] = value103 safe_data[key] = value 98 104 self._cache.set_multi(safe_data, self._get_memcache_timeout(timeout)) 99 105 100 106 def delete_many(self, keys): 101 self._cache.delete_multi(map(s mart_str, keys))107 self._cache.delete_multi(map(self.make_key, keys)) 102 108 103 109 def clear(self): 104 110 self._cache.flush_all() -
docs/topics/cache.txt
680 680 ...and use the dotted Python path to this module as the scheme portion of your 681 681 :setting:`CACHE_BACKEND`. 682 682 683 CACHE_KEY_PREFIX 684 ---------------- 685 686 It is a common occurence to have a shared cache instance running on your development 687 or production server for use across multiple projects, i.e. all sites are pointing 688 to the memcached instance running on port 11211. As a result, cache key conflicts 689 may arise which could result in data from one site being used by another. 690 691 To alleviate this, CACHE_KEY_PREFIX can be set. This will be prepended to all keys 692 that are used with the cache backend, transparently:: 693 694 # in settings, CACHE_KEY_PREFIX = 'myproject_' 695 696 >>> cache.set('my_key', 'hello world!') # set with key 'myproject_my_key' 697 >>> cache.get('my_key') # retrieved with key 'myproject_my_key' 698 'hello world!' 699 700 Of course, for sites that _do_ share content, simply set the CACHE_KEY_PREFIX for 701 both sites to the same value. The default value for CACHE_KEY_PREFIX is the empty 702 string ``''``. 703 704 .. note:: 705 706 This does *not* conflict with the CACHE_MIDDLEWARE_KEY_PREFIX and can be used 707 in conjunction with it. CACHE_KEY_PREFIX acts as a global prefix for a 708 particular cache instance, therefore it will be prepended to the 709 CACHE_MIDDLEWARE_KEY_PREFIX transparently. 710 683 711 Upstream caches 684 712 =============== 685 713 -
tests/regressiontests/cache/tests.py
144 144 "clear does nothing for the dummy cache backend" 145 145 self.cache.clear() 146 146 147 148 147 class BaseCacheTests(object): 149 148 # A common set of tests to apply to all cache backends 150 149 def tearDown(self): … … 162 161 self.assertEqual(result, False) 163 162 self.assertEqual(self.cache.get("addkey1"), "value") 164 163 164 def test_prefix(self): 165 # Test for same cache key conflicts between shared backend 166 self.cache.set('somekey', 'value') 167 168 # should not be set in the prefixed cache 169 self.assertFalse(self.pfx_cache.has_key('somekey')) 170 171 self.pfx_cache.set('somekey', 'value2') 172 173 self.assertEqual(self.cache.get('somekey'), 'value') 174 self.assertEqual(self.pfx_cache.get('somekey'), 'value2') 175 165 176 def test_non_existent(self): 166 177 # Non-existent cache keys return as None/default 167 178 # get with non-existent keys … … 400 411 self._table_name = 'test cache table' 401 412 management.call_command('createcachetable', self._table_name, verbosity=0, interactive=False) 402 413 self.cache = get_cache('db://%s?max_entries=30' % self._table_name) 414 self.pfx_cache = get_cache('db://%s' % self._table_name, 'cacheprefix') 403 415 404 416 def tearDown(self): 405 417 from django.db import connection … … 412 424 class LocMemCacheTests(unittest.TestCase, BaseCacheTests): 413 425 def setUp(self): 414 426 self.cache = get_cache('locmem://?max_entries=30') 427 self.pfx_cache = get_cache('locmem://', 'cacheprefix') 415 428 416 429 def test_cull(self): 417 430 self.perform_cull_test(50, 29) … … 424 437 class MemcachedCacheTests(unittest.TestCase, BaseCacheTests): 425 438 def setUp(self): 426 439 self.cache = get_cache(settings.CACHE_BACKEND) 440 self.pfx_cache = get_cache(settings.CACHE_BACKEND, 'cacheprefix') 427 441 428 442 def test_invalid_keys(self): 429 443 """ … … 448 462 def setUp(self): 449 463 self.dirname = tempfile.mkdtemp() 450 464 self.cache = get_cache('file://%s?max_entries=30' % self.dirname) 465 self.pfx_cache = get_cache('file://%s' % self.dirname, 'cacheprefix') 451 466 452 467 def test_hashing(self): 453 468 """Test that keys are hashed into subdirectories correctly""" 454 469 self.cache.set("foo", "bar") 455 keyhash = md5_constructor("foo").hexdigest() 470 key = self.cache.make_key("foo") 471 keyhash = md5_constructor(key).hexdigest() 456 472 keypath = os.path.join(self.dirname, keyhash[:2], keyhash[2:4], keyhash[4:]) 457 473 self.assert_(os.path.exists(keypath)) 458 474 … … 461 477 Make sure that the created subdirectories are correctly removed when empty. 462 478 """ 463 479 self.cache.set("foo", "bar") 464 keyhash = md5_constructor("foo").hexdigest() 480 key = self.cache.make_key("foo") 481 keyhash = md5_constructor(key).hexdigest() 465 482 keypath = os.path.join(self.dirname, keyhash[:2], keyhash[2:4], keyhash[4:]) 466 483 self.assert_(os.path.exists(keypath)) 467 484 … … 494 511 495 512 def setUp(self): 496 513 self.path = '/cache/test/' 497 self.old_ settings_key_prefix = settings.CACHE_MIDDLEWARE_KEY_PREFIX498 self.old_ middleware_seconds = settings.CACHE_MIDDLEWARE_SECONDS514 self.old_cache_middleware_key_prefix = settings.CACHE_MIDDLEWARE_KEY_PREFIX 515 self.old_cache_middleware_seconds = settings.CACHE_MIDDLEWARE_SECONDS 499 516 self.orig_use_i18n = settings.USE_I18N 500 517 settings.CACHE_MIDDLEWARE_KEY_PREFIX = 'settingsprefix' 501 518 settings.CACHE_MIDDLEWARE_SECONDS = 1 502 519 settings.USE_I18N = False 503 520 504 521 def tearDown(self): 505 settings.CACHE_MIDDLEWARE_KEY_PREFIX = self.old_ settings_key_prefix506 settings.CACHE_MIDDLEWARE_SECONDS = self.old_ middleware_seconds522 settings.CACHE_MIDDLEWARE_KEY_PREFIX = self.old_cache_middleware_key_prefix 523 settings.CACHE_MIDDLEWARE_SECONDS = self.old_cache_middleware_seconds 507 524 settings.USE_I18N = self.orig_use_i18n 508 525 509 526 def _get_request(self, path): … … 556 573 learn_cache_key(request, response) 557 574 self.assertEqual(get_cache_key(request), 'views.decorators.cache.cache_page.settingsprefix.a8c87a3d8c44853d7f79474f7ffe4ad5.d41d8cd98f00b204e9800998ecf8427e') 558 575 576 class PrefixedCacheUtils(CacheUtils): 577 def setUp(self): 578 super(PrefixedCacheUtils, self).setUp() 579 self.old_cache_key_prefix = settings.CACHE_KEY_PREFIX 580 settings.CACHE_KEY_PREFIX = 'cacheprefix' 581 582 def tearDown(self): 583 super(PrefixedCacheUtils, self).tearDown() 584 settings.CACHE_KEY_PREFIX = self.old_cache_key_prefix 585 559 586 class CacheI18nTest(unittest.TestCase): 560 587 561 588 def setUp(self): … … 651 678 get_cache_data = FetchFromCacheMiddleware().process_request(request) 652 679 self.assertEqual(get_cache_data.content, es_message) 653 680 681 class PrefixedCacheI18nTest(CacheI18nTest): 682 def setUp(self): 683 super(PrefixedCacheI18nTest, self).setUp() 684 self.old_cache_key_prefix = settings.CACHE_KEY_PREFIX 685 settings.CACHE_KEY_PREFIX = 'cacheprefix' 686 687 def tearDown(self): 688 super(PrefixedCacheI18nTest, self).tearDown() 689 settings.CACHE_KEY_PREFIX = self.old_cache_key_prefix 690 654 691 if __name__ == '__main__': 655 692 unittest.main()