Ticket #13795: cache_key_prefix_13357.diff

File cache_key_prefix_13357.diff, 16.4 KB (added by bruth, 5 years ago)
  • django/conf/global_settings.py

     
    427427# The cache backend to use.  See the docstring in django.core.cache for the
    428428# possible values.
    429429CACHE_BACKEND = 'locmem://'
     430CACHE_KEY_PREFIX = ''
    430431CACHE_MIDDLEWARE_KEY_PREFIX = ''
    431432CACHE_MIDDLEWARE_SECONDS = 600
    432433
  • django/core/cache/__init__.py

     
    5656
    5757    return scheme, host, params
    5858
    59 def get_cache(backend_uri):
     59def get_cache(backend_uri, key_prefix=''):
    6060    scheme, host, params = parse_backend_uri(backend_uri)
    6161    if scheme in BACKENDS:
    6262        name = 'django.core.cache.backends.%s' % BACKENDS[scheme]
    6363    else:
    6464        name = scheme
    6565    module = importlib.import_module(name)
    66     return getattr(module, 'CacheClass')(host, params)
     66    return getattr(module, 'CacheClass')(host, params, key_prefix)
    6767
    68 cache = get_cache(settings.CACHE_BACKEND)
     68cache = get_cache(settings.CACHE_BACKEND, settings.CACHE_KEY_PREFIX)
    6969
    7070# Some caches -- pythont-memcached in particular -- need to do a cleanup at the
    7171# end of a request cycle. If the cache provides a close() method, wire it up
  • django/core/cache/backends/base.py

     
    11"Base Cache class."
    22
    33from django.core.exceptions import ImproperlyConfigured
     4from django.utils.encoding import smart_str
    45
    56class InvalidCacheBackendError(ImproperlyConfigured):
    67    pass
    78
    89class BaseCache(object):
    9     def __init__(self, params):
     10    def __init__(self, params, key_prefix=''):
    1011        timeout = params.get('timeout', 300)
    1112        try:
    1213            timeout = int(timeout)
    1314        except (ValueError, TypeError):
    1415            timeout = 300
    1516        self.default_timeout = timeout
     17        self.key_prefix = smart_str(key_prefix)
    1618
     19    def _key(self, key):
     20        "Returns the key prepended with the `cache_prefix'."
     21        return self.key_prefix + smart_str(key)
     22
    1723    def add(self, key, value, timeout=None):
    1824        """
    1925        Set a value in the cache if the key does not already exist. If
  • django/core/cache/backends/locmem.py

     
    1010from django.utils.synch import RWLock
    1111
    1212class 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)
    1515        self._cache = {}
    1616        self._expire_info = {}
    1717
     
    3030        self._lock = RWLock()
    3131
    3232    def add(self, key, value, timeout=None):
     33        key = self._key(key)
    3334        self._lock.writer_enters()
    3435        try:
    3536            exp = self._expire_info.get(key)
     
    4445            self._lock.writer_leaves()
    4546
    4647    def get(self, key, default=None):
     48        key = self._key(key)
    4749        self._lock.reader_enters()
    4850        try:
    4951            exp = self._expire_info.get(key)
     
    7678        self._expire_info[key] = time.time() + timeout
    7779
    7880    def set(self, key, value, timeout=None):
     81        key = self._key(key)
    7982        self._lock.writer_enters()
    8083        # Python 2.4 doesn't allow combined try-except-finally blocks.
    8184        try:
     
    8790            self._lock.writer_leaves()
    8891
    8992    def has_key(self, key):
     93        key = self._key(key)
    9094        self._lock.reader_enters()
    9195        try:
    9296            exp = self._expire_info.get(key)
     
    127131            pass
    128132
    129133    def delete(self, key):
     134        key = self._key(key)
    130135        self._lock.writer_enters()
    131136        try:
    132137            self._delete(key)
  • django/core/cache/backends/filebased.py

     
    1212from django.utils.hashcompat import md5_constructor
    1313
    1414class 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)
    1717
    1818        max_entries = params.get('max_entries', 300)
    1919        try:
     
    3232            self._createdir()
    3333
    3434    def add(self, key, value, timeout=None):
     35        key = self._key(key)
    3536        if self.has_key(key):
    3637            return False
    3738
     
    3940        return True
    4041
    4142    def get(self, key, default=None):
     43        key = self._key(key)
    4244        fname = self._key_to_file(key)
    4345        try:
    4446            f = open(fname, 'rb')
     
    5658        return default
    5759
    5860    def set(self, key, value, timeout=None):
     61        key = self._key(key)
    5962        fname = self._key_to_file(key)
    6063        dirname = os.path.dirname(fname)
    6164
     
    7982            pass
    8083
    8184    def delete(self, key):
     85        key = self._key(key)
    8286        try:
    8387            self._delete(self._key_to_file(key))
    8488        except (IOError, OSError):
     
    9599            pass
    96100
    97101    def has_key(self, key):
     102        key = self._key(key)
    98103        fname = self._key_to_file(key)
    99104        try:
    100105            f = open(fname, 'rb')
     
    148153        Thus, a cache key of "foo" gets turnned into a file named
    149154        ``{cache-dir}ac/bd/18db4cc2f85cedef654fccc4a4d8``.
    150155        """
    151         path = md5_constructor(key.encode('utf-8')).hexdigest()
     156        key = self._key(key)
     157        path = md5_constructor(key).hexdigest()
    152158        path = os.path.join(path[:2], path[2:4], path[4:])
    153159        return os.path.join(self._dir, path)
    154160
  • django/core/cache/backends/db.py

     
    1010    import pickle
    1111
    1212class 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)
    1515        self._table = connection.ops.quote_name(table)
    1616        max_entries = params.get('max_entries', 300)
    1717        try:
     
    2525            self._cull_frequency = 3
    2626
    2727    def get(self, key, default=None):
     28        key = self._key(key)
    2829        cursor = connection.cursor()
    2930        cursor.execute("SELECT cache_key, value, expires FROM %s WHERE cache_key = %%s" % self._table, [key])
    3031        row = cursor.fetchone()
     
    4546        return self._base_set('add', key, value, timeout)
    4647
    4748    def _base_set(self, mode, key, value, timeout=None):
     49        key = self._key(key)
    4850        if timeout is None:
    4951            timeout = self.default_timeout
    5052        cursor = connection.cursor()
     
    7476            return True
    7577
    7678    def delete(self, key):
     79        key = self._key(key)
    7780        cursor = connection.cursor()
    7881        cursor.execute("DELETE FROM %s WHERE cache_key = %%s" % self._table, [key])
    7982        transaction.commit_unless_managed()
    8083
    8184    def has_key(self, key):
     85        key = self._key(key)
    8286        now = datetime.now().replace(microsecond=0)
    8387        cursor = connection.cursor()
    8488        cursor.execute("SELECT cache_key FROM %s WHERE cache_key = %%s and expires > %%s" % self._table,
  • django/core/cache/backends/memcached.py

     
    33import time
    44
    55from django.core.cache.backends.base import BaseCache, InvalidCacheBackendError
    6 from django.utils.encoding import smart_unicode, smart_str
    76
    87try:
    98    import cmemcache as memcache
     
    1918        raise InvalidCacheBackendError("Memcached cache backend requires either the 'memcache' or 'cmemcache' library")
    2019
    2120class 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)
    2423        self._cache = memcache.Client(server.split(';'))
    2524
    2625    def _get_memcache_timeout(self, timeout):
     
    4039        return timeout
    4140
    4241    def add(self, key, value, timeout=0):
     42        key = self._key(key)
    4343        if isinstance(value, unicode):
    4444            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))
    4646
    4747    def get(self, key, default=None):
    48         val = self._cache.get(smart_str(key))
     48        key = self._key(key)
     49        val = self._cache.get(key)
    4950        if val is None:
    5051            return default
    5152        return val
    5253
    5354    def set(self, key, value, timeout=0):
    54         self._cache.set(smart_str(key), value, self._get_memcache_timeout(timeout))
     55        key = self._key(key)
     56        self._cache.set(key, value, self._get_memcache_timeout(timeout))
    5557
    5658    def delete(self, key):
    57         self._cache.delete(smart_str(key))
     59        key = self._key(key)
     60        self._cache.delete(key)
    5861
    5962    def get_many(self, keys):
    60         return self._cache.get_multi(map(smart_str,keys))
     63        keys = map(self._key, keys)
     64        return self._cache.get_multi(keys)
    6165
    6266    def close(self, **kwargs):
    6367        self._cache.disconnect_all()
    6468
    6569    def incr(self, key, delta=1):
     70        key = self._key(key)
    6671        try:
    6772            val = self._cache.incr(key, delta)
    6873
     
    7782        return val
    7883
    7984    def decr(self, key, delta=1):
     85        key = self._key(key)
    8086        try:
    8187            val = self._cache.decr(key, delta)
    8288
     
    9298    def set_many(self, data, timeout=0):
    9399        safe_data = {}
    94100        for key, value in data.items():
     101            key = self._key(key)
    95102            if isinstance(value, unicode):
    96103                value = value.encode('utf-8')
    97             safe_data[smart_str(key)] = value
     104            safe_data[key] = value
    98105        self._cache.set_multi(safe_data, self._get_memcache_timeout(timeout))
    99106
    100107    def delete_many(self, keys):
    101         self._cache.delete_multi(map(smart_str, keys))
     108        keys = map(self._key, keys)
     109        self._cache.delete_multi(keys)
    102110
    103111    def clear(self):
    104112        self._cache.flush_all()
  • tests/regressiontests/cache/tests.py

     
    143143        "clear does nothing for the dummy cache backend"
    144144        self.cache.clear()
    145145
     146class PrefixedDummyCacheTests(DummyCacheTests):
     147    def setUp(self):
     148        self.cache = get_cache('dummy://', 'dummy_prefix')
    146149
    147150class BaseCacheTests(object):
    148151    # A common set of tests to apply to all cache backends
     
    364367        cursor = connection.cursor()
    365368        cursor.execute('DROP TABLE %s' % connection.ops.quote_name(self._table_name))
    366369
     370class PrefixedDBCacheTests(DBCacheTests):
     371    def setUp(self):
     372        self._table_name = 'test cache table'
     373        management.call_command('createcachetable', self._table_name, verbosity=0, interactive=False)
     374        self.cache = get_cache('db://%s' % self._table_name, 'db_prefix')
     375
    367376class LocMemCacheTests(unittest.TestCase, BaseCacheTests):
    368377    def setUp(self):
    369378        self.cache = get_cache('locmem://')
    370379
     380class PrefixedLocMemCacheTests(LocMemCacheTests):
     381    def setUp(self):
     382        self.cache = get_cache('locmem://', 'locmem_prefix')
     383
    371384# memcached backend isn't guaranteed to be available.
    372385# To check the memcached backend, the test settings file will
    373386# need to contain a CACHE_BACKEND setting that points at
     
    377390        def setUp(self):
    378391            self.cache = get_cache(settings.CACHE_BACKEND)
    379392
     393    class PrefixedMemcacheCacheTests(MemcachedCacheTests):
     394        def setUp(self):
     395            self.cache = get_cache(settings.CACHE_BACKEND, 'memcache_prefix')
     396
    380397class FileBasedCacheTests(unittest.TestCase, BaseCacheTests):
    381398    """
    382399    Specific test cases for the file-based cache.
     
    406423        self.assert_(not os.path.exists(os.path.dirname(keypath)))
    407424        self.assert_(not os.path.exists(os.path.dirname(os.path.dirname(keypath))))
    408425
     426class PrefixedFileBasedCacheTests(FileBasedCacheTests):
     427    def setUp(self):
     428        self.dirname = tempfile.mkdtemp()
     429        self.cache = get_cache('file://%s' % self.dirname)
     430
    409431class CacheUtils(unittest.TestCase):
    410432    """TestCase for django.utils.cache functions."""
    411433
    412434    def setUp(self):
    413435        self.path = '/cache/test/'
    414         self.old_settings_key_prefix = settings.CACHE_MIDDLEWARE_KEY_PREFIX
    415         self.old_middleware_seconds = settings.CACHE_MIDDLEWARE_SECONDS
     436        self.old_cache_middleware_key_prefix = settings.CACHE_MIDDLEWARE_KEY_PREFIX
     437        self.old_cache_middleware_seconds = settings.CACHE_MIDDLEWARE_SECONDS
    416438        self.orig_use_i18n = settings.USE_I18N
    417439        settings.CACHE_MIDDLEWARE_KEY_PREFIX = 'settingsprefix'
    418440        settings.CACHE_MIDDLEWARE_SECONDS = 1
    419441        settings.USE_I18N = False
    420442
    421443    def tearDown(self):
    422         settings.CACHE_MIDDLEWARE_KEY_PREFIX = self.old_settings_key_prefix
    423         settings.CACHE_MIDDLEWARE_SECONDS = self.old_middleware_seconds
     444        settings.CACHE_MIDDLEWARE_KEY_PREFIX = self.old_cache_middleware_key_prefix
     445        settings.CACHE_MIDDLEWARE_SECONDS = self.old_cache_middleware_seconds
    424446        settings.USE_I18N = self.orig_use_i18n
    425447
    426448    def _get_request(self, path):
     
    473495        learn_cache_key(request, response)
    474496        self.assertEqual(get_cache_key(request), 'views.decorators.cache.cache_page.settingsprefix.a8c87a3d8c44853d7f79474f7ffe4ad5.d41d8cd98f00b204e9800998ecf8427e')
    475497
     498class PrefixedCacheUtils(CacheUtils):
     499    def setUp(self):
     500        super(PrefixedCacheUtils, self).setUp()
     501        self.old_cache_key_prefix = settings.CACHE_KEY_PREFIX
     502        settings.CACHE_KEY_PREFIX = 'cacheprefix'
     503
     504    def tearDown(self):
     505        super(PrefixedCacheUtils, self).tearDown()
     506        settings.CACHE_KEY_PREFIX = self.old_cache_key_prefix
     507
    476508class CacheI18nTest(unittest.TestCase):
    477509
    478510    def setUp(self):
     
    568600        get_cache_data = FetchFromCacheMiddleware().process_request(request)
    569601        self.assertEqual(get_cache_data.content, es_message)
    570602
     603class PrefixedCacheI18nTest(CacheI18nTest):
     604    def setUp(self):
     605        super(PrefixedCacheI18nTest, self).setUp()
     606        self.old_cache_key_prefix = settings.CACHE_KEY_PREFIX
     607        settings.CACHE_KEY_PREFIX = 'cacheprefix'
     608
     609    def tearDown(self):
     610        super(PrefixedCacheI18nTest, self).tearDown()
     611        settings.CACHE_KEY_PREFIX = self.old_cache_key_prefix
     612
    571613if __name__ == '__main__':
    572614    unittest.main()
  • docs/topics/cache.txt

     
    600600    However, if the backend doesn't natively provide an increment/decrement
    601601    operation, it will be implemented using a two-step retrieve/update.
    602602
     603CACHE_KEY_PREFIX
     604----------------
     605
     606It is a common occurence to have a shared cache instance running on your development
     607or production server for use across multiple projects, i.e. all sites are pointing
     608to the memcached instance running on port 11211. As a result, cache key conflicts
     609may arise which could result in data from one site being used by another.
     610
     611To alleviate this, CACHE_KEY_PREFIX can be set. This will be prepended to all keys
     612that 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
     620Of course, for sites that _do_ share content, simply set the CACHE_KEY_PREFIX for
     621both sites to the same value. The default value for CACHE_KEY_PREFIX is the empty
     622string ``''``.
     623
    603624Upstream caches
    604625===============
    605626
Back to Top