Ticket #13795: t13795-rc1.diff

File t13795-rc1.diff, 48.1 KB (added by Russell Keith-Magee, 13 years ago)

RC1 of site-wide caching prefix

  • django/conf/global_settings.py

    diff -r 9aed8047e568 django/conf/global_settings.py
    a b  
    433433# The cache backend to use.  See the docstring in django.core.cache for the
    434434# possible values.
    435435CACHE_BACKEND = 'locmem://'
     436CACHE_VERSION = 1
     437CACHE_KEY_PREFIX = ''
     438CACHE_KEY_FUNCTION = None
    436439CACHE_MIDDLEWARE_KEY_PREFIX = ''
    437440CACHE_MIDDLEWARE_SECONDS = 600
    438441
  • django/core/cache/__init__.py

    diff -r 9aed8047e568 django/core/cache/__init__.py
    a b  
    6767
    6868    return scheme, host, params
    6969
    70 def get_cache(backend_uri):
     70def get_cache(backend_uri, key_prefix=None, version=None, key_func=None):
     71    if key_prefix is None:
     72        key_prefix = settings.CACHE_KEY_PREFIX
     73    if version is None:
     74        version = settings.CACHE_VERSION
     75    if key_func is None:
     76        key_func = settings.CACHE_KEY_FUNCTION
     77
     78    if key_func is not None and not callable(key_func):
     79        key_func_module_path, key_func_name = settings.CACHE_KEY_FUNCTION.rsplit('.', 1)
     80        key_func_module = importlib.import_module(key_func_module_path)
     81        key_func = getattr(key_func_module, key_func_name)
     82
    7183    scheme, host, params = parse_backend_uri(backend_uri)
    7284    if scheme in BACKENDS:
    7385        name = 'django.core.cache.backends.%s' % BACKENDS[scheme]
    7486    else:
    7587        name = scheme
    7688    module = importlib.import_module(name)
    77     return module.CacheClass(host, params)
     89    return module.CacheClass(host, params, key_prefix, version, key_func)
    7890
    7991cache = get_cache(settings.CACHE_BACKEND)
    8092
    81 # Some caches -- pythont-memcached in particular -- need to do a cleanup at the
     93# Some caches -- python-memcached in particular -- need to do a cleanup at the
    8294# end of a request cycle. If the cache provides a close() method, wire it up
    8395# here.
    8496if hasattr(cache, 'close'):
  • django/core/cache/backends/base.py

    diff -r 9aed8047e568 django/core/cache/backends/base.py
    a b  
    33import warnings
    44
    55from django.core.exceptions import ImproperlyConfigured, DjangoRuntimeWarning
     6from django.utils.encoding import smart_str
    67
    78class InvalidCacheBackendError(ImproperlyConfigured):
    89    pass
     
    1314# Memcached does not accept keys longer than this.
    1415MEMCACHE_MAX_KEY_LENGTH = 250
    1516
     17def make_key(key, key_prefix, version):
     18    """Default function to generate keys.
     19
     20    Constructs the key used by all other methods. By default it prepends
     21    the `key_prefix'. CACHE_KEY_FUNCTION can be used to specify an alternate
     22    function with custom key making behavior.
     23    """
     24    return ':'.join([key_prefix, str(version), smart_str(key)])
     25
    1626class BaseCache(object):
    17     def __init__(self, params):
     27    def __init__(self, params, key_prefix='', version=1, key_func=None):
    1828        timeout = params.get('timeout', 300)
    1929        try:
    2030            timeout = int(timeout)
     
    3444        except (ValueError, TypeError):
    3545            self._cull_frequency = 3
    3646
    37     def add(self, key, value, timeout=None):
     47        self.key_prefix = smart_str(key_prefix)
     48        self.version = version
     49        self.key_func = key_func or make_key
     50
     51    def _make_key(self, key, version=None):
     52        """Constructs the key used by all other methods. By default it prepends
     53        the `key_prefix'. In cache backend subclasses this can be overriden to
     54        provide custom key making behavior.
     55        """
     56        if version is None:
     57            version = self.version
     58
     59        new_key = self.key_func(key, self.key_prefix, version)
     60        self.validate_key(new_key)
     61        return new_key
     62
     63    def add(self, key, value, timeout=None, version=None):
    3864        """
    3965        Set a value in the cache if the key does not already exist. If
    4066        timeout is given, that timeout will be used for the key; otherwise
     
    4470        """
    4571        raise NotImplementedError
    4672
    47     def get(self, key, default=None):
     73    def get(self, key, default=None, version=None):
    4874        """
    4975        Fetch a given key from the cache. If the key does not exist, return
    5076        default, which itself defaults to None.
    5177        """
    5278        raise NotImplementedError
    5379
    54     def set(self, key, value, timeout=None):
     80    def set(self, key, value, timeout=None, version=None):
    5581        """
    5682        Set a value in the cache. If timeout is given, that timeout will be
    5783        used for the key; otherwise the default cache timeout will be used.
    5884        """
    5985        raise NotImplementedError
    6086
    61     def delete(self, key):
     87    def delete(self, key, version=None):
    6288        """
    6389        Delete a key from the cache, failing silently.
    6490        """
    6591        raise NotImplementedError
    6692
    67     def get_many(self, keys):
     93    def get_many(self, keys, version=None):
    6894        """
    6995        Fetch a bunch of keys from the cache. For certain backends (memcached,
    7096        pgsql) this can be *much* faster when fetching multiple values.
     
    74100        """
    75101        d = {}
    76102        for k in keys:
    77             val = self.get(k)
     103            val = self.get(k, version=version)
    78104            if val is not None:
    79105                d[k] = val
    80106        return d
    81107
    82     def has_key(self, key):
     108    def has_key(self, key, version=None):
    83109        """
    84110        Returns True if the key is in the cache and has not expired.
    85111        """
    86         return self.get(key) is not None
     112        return self.get(key, version=version) is not None
    87113
    88     def incr(self, key, delta=1):
     114    def incr(self, key, delta=1, version=None):
    89115        """
    90116        Add delta to value in the cache. If the key does not exist, raise a
    91117        ValueError exception.
    92118        """
    93         if key not in self:
     119        value = self.get(key, version=version)
     120        if value is None:
    94121            raise ValueError("Key '%s' not found" % key)
    95         new_value = self.get(key) + delta
    96         self.set(key, new_value)
     122        new_value = value + delta
     123        self.set(key, new_value, version=version)
    97124        return new_value
    98125
    99     def decr(self, key, delta=1):
     126    def decr(self, key, delta=1, version=None):
    100127        """
    101128        Subtract delta from value in the cache. If the key does not exist, raise
    102129        a ValueError exception.
    103130        """
    104         return self.incr(key, -delta)
     131        return self.incr(key, -delta, version=version)
    105132
    106133    def __contains__(self, key):
    107134        """
     
    112139        # if a subclass overrides it.
    113140        return self.has_key(key)
    114141
    115     def set_many(self, data, timeout=None):
     142    def set_many(self, data, timeout=None, version=None):
    116143        """
    117144        Set a bunch of values in the cache at once from a dict of key/value
    118145        pairs.  For certain backends (memcached), this is much more efficient
     
    122149        the default cache timeout will be used.
    123150        """
    124151        for key, value in data.items():
    125             self.set(key, value, timeout)
     152            self.set(key, value, timeout=timeout, version=version)
    126153
    127     def delete_many(self, keys):
     154    def delete_many(self, keys, version=None):
    128155        """
    129156        Set a bunch of values in the cache at once.  For certain backends
    130157        (memcached), this is much more efficient than calling delete() multiple
    131158        times.
    132159        """
    133160        for key in keys:
    134             self.delete(key)
     161            self.delete(key, version=version)
    135162
    136163    def clear(self):
    137164        """Remove *all* values from the cache at once."""
     
    154181                        'errors if used with memcached: %r' % key,
    155182                              CacheKeyWarning)
    156183
     184    def incr_version(self, key, delta=1, version=None):
     185        """Adds delta to the cache version for the supplied key. Returns the
     186        new version.
     187        """
     188        if version is None:
     189            version = self.version
     190
     191        value = self.get(key, version=version)
     192        if value is None:
     193            raise ValueError("Key '%s' not found" % key)
     194
     195        self.set(key, value, version=version+delta)
     196        self.delete(key, version=version)
     197        return version+delta
     198
     199    def decr_version(self, key, delta=1, version=None):
     200        """Substracts delta from the cache version for the supplied key. Returns
     201        the new version.
     202        """
     203        return self.incr_version(key, -delta, version)
  • django/core/cache/backends/db.py

    diff -r 9aed8047e568 django/core/cache/backends/db.py
    a b  
    2626        self.proxy = False
    2727
    2828class BaseDatabaseCacheClass(BaseCache):
    29     def __init__(self, table, params):
    30         BaseCache.__init__(self, params)
     29    def __init__(self, table, params, key_prefix='', version=1, key_func=None):
     30        BaseCache.__init__(self, params, key_prefix, version, key_func)
    3131        self._table = table
    3232
    3333        class CacheEntry(object):
     
    3535        self.cache_model_class = CacheEntry
    3636
    3737class CacheClass(BaseDatabaseCacheClass):
    38     def get(self, key, default=None):
    39         self.validate_key(key)
     38    def get(self, key, default=None, version=None):
     39        key = self._make_key(key, version=version)
    4040        db = router.db_for_read(self.cache_model_class)
    4141        table = connections[db].ops.quote_name(self._table)
    4242        cursor = connections[db].cursor()
     
    5555        value = connections[db].ops.process_clob(row[1])
    5656        return pickle.loads(base64.decodestring(value))
    5757
    58     def set(self, key, value, timeout=None):
    59         self.validate_key(key)
     58    def set(self, key, value, timeout=None, version=None):
     59        key = self._make_key(key, version=version)
    6060        self._base_set('set', key, value, timeout)
    6161
    62     def add(self, key, value, timeout=None):
    63         self.validate_key(key)
     62    def add(self, key, value, timeout=None, version=None):
     63        key = self._make_key(key, version=version)
    6464        return self._base_set('add', key, value, timeout)
    6565
    6666    def _base_set(self, mode, key, value, timeout=None):
     
    9595            transaction.commit_unless_managed(using=db)
    9696            return True
    9797
    98     def delete(self, key):
    99         self.validate_key(key)
     98    def delete(self, key, version=None):
     99        key = self._make_key(key, version=version)
     100
    100101        db = router.db_for_write(self.cache_model_class)
    101102        table = connections[db].ops.quote_name(self._table)
    102103        cursor = connections[db].cursor()
     
    104105        cursor.execute("DELETE FROM %s WHERE cache_key = %%s" % table, [key])
    105106        transaction.commit_unless_managed(using=db)
    106107
    107     def has_key(self, key):
    108         self.validate_key(key)
     108    def has_key(self, key, version=None):
     109        key = self._make_key(key, version=version)
     110
    109111        db = router.db_for_read(self.cache_model_class)
    110112        table = connections[db].ops.quote_name(self._table)
    111113        cursor = connections[db].cursor()
  • django/core/cache/backends/dummy.py

    diff -r 9aed8047e568 django/core/cache/backends/dummy.py
    a b  
    33from django.core.cache.backends.base import BaseCache
    44
    55class CacheClass(BaseCache):
    6     def __init__(self, *args, **kwargs):
    7         pass
     6    def __init__(self, host, *args, **kwargs):
     7        BaseCache.__init__(self, *args, **kwargs)
    88
    99    def add(self, key, *args, **kwargs):
    10         self.validate_key(key)
     10        self._make_key(key)
    1111        return True
    1212
    13     def get(self, key, default=None):
    14         self.validate_key(key)
     13    def get(self, key, default=None, version=None):
     14        self._make_key(key)
    1515        return default
    1616
    1717    def set(self, key, *args, **kwargs):
    18         self.validate_key(key)
     18        self._make_key(key)
    1919
    2020    def delete(self, key, *args, **kwargs):
    21         self.validate_key(key)
     21        self._make_key(key)
    2222
    2323    def get_many(self, *args, **kwargs):
    2424        return {}
    2525
    2626    def has_key(self, key, *args, **kwargs):
    27         self.validate_key(key)
     27        self._make_key(key)
    2828        return False
    2929
    3030    def set_many(self, *args, **kwargs):
  • django/core/cache/backends/filebased.py

    diff -r 9aed8047e568 django/core/cache/backends/filebased.py
    a b  
    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='', version=1, key_func=None):
     16        BaseCache.__init__(self, params, key_prefix, version, key_func)
    1717        self._dir = dir
    1818        if not os.path.exists(self._dir):
    1919            self._createdir()
    2020
    21     def add(self, key, value, timeout=None):
    22         self.validate_key(key)
    23         if self.has_key(key):
     21    def add(self, key, value, timeout=None, version=None):
     22        if self.has_key(key, version=version):
    2423            return False
    2524
    26         self.set(key, value, timeout)
     25        self.set(key, value, timeout, version=version)
    2726        return True
    2827
    29     def get(self, key, default=None):
    30         self.validate_key(key)
     28    def get(self, key, default=None, version=None):
     29        key = self._make_key(key, version=version)
     30
    3131        fname = self._key_to_file(key)
    3232        try:
    3333            f = open(fname, 'rb')
     
    4444            pass
    4545        return default
    4646
    47     def set(self, key, value, timeout=None):
    48         self.validate_key(key)
     47    def set(self, key, value, timeout=None, version=None):
     48        key = self._make_key(key, version=version)
     49
    4950        fname = self._key_to_file(key)
    5051        dirname = os.path.dirname(fname)
    5152
     
    6869        except (IOError, OSError):
    6970            pass
    7071
    71     def delete(self, key):
    72         self.validate_key(key)
     72    def delete(self, key, version=None):
     73        key = self._make_key(key, version=version)
    7374        try:
    7475            self._delete(self._key_to_file(key))
    7576        except (IOError, OSError):
     
    8586        except (IOError, OSError):
    8687            pass
    8788
    88     def has_key(self, key):
    89         self.validate_key(key)
     89    def has_key(self, key, version=None):
     90        key = self._make_key(key, version=version)
    9091        fname = self._key_to_file(key)
    9192        try:
    9293            f = open(fname, 'rb')
     
    140141        Thus, a cache key of "foo" gets turnned into a file named
    141142        ``{cache-dir}ac/bd/18db4cc2f85cedef654fccc4a4d8``.
    142143        """
    143         path = md5_constructor(key.encode('utf-8')).hexdigest()
     144        path = md5_constructor(key).hexdigest()
    144145        path = os.path.join(path[:2], path[2:4], path[4:])
    145146        return os.path.join(self._dir, path)
    146147
  • django/core/cache/backends/locmem.py

    diff -r 9aed8047e568 django/core/cache/backends/locmem.py
    a b  
    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='', version=1, key_func=None):
     14        BaseCache.__init__(self, params, key_prefix, version, key_func)
    1515        self._cache = {}
    1616        self._expire_info = {}
    1717        self._lock = RWLock()
    1818
    19     def add(self, key, value, timeout=None):
    20         self.validate_key(key)
     19    def add(self, key, value, timeout=None, version=None):
     20        key = self._make_key(key, version=version)
    2121        self._lock.writer_enters()
    2222        try:
    2323            exp = self._expire_info.get(key)
     
    3131        finally:
    3232            self._lock.writer_leaves()
    3333
    34     def get(self, key, default=None):
    35         self.validate_key(key)
     34    def get(self, key, default=None, version=None):
     35        key = self._make_key(key, version=version)
    3636        self._lock.reader_enters()
    3737        try:
    3838            exp = self._expire_info.get(key)
     
    6464        self._cache[key] = value
    6565        self._expire_info[key] = time.time() + timeout
    6666
    67     def set(self, key, value, timeout=None):
    68         self.validate_key(key)
     67    def set(self, key, value, timeout=None, version=None):
     68        key = self._make_key(key, version=version)
    6969        self._lock.writer_enters()
    7070        # Python 2.4 doesn't allow combined try-except-finally blocks.
    7171        try:
     
    7676        finally:
    7777            self._lock.writer_leaves()
    7878
    79     def has_key(self, key):
    80         self.validate_key(key)
     79    def has_key(self, key, version=None):
     80        key = self._make_key(key, version=version)
    8181        self._lock.reader_enters()
    8282        try:
    8383            exp = self._expire_info.get(key)
     
    117117        except KeyError:
    118118            pass
    119119
    120     def delete(self, key):
    121         self.validate_key(key)
     120    def delete(self, key, version=None):
     121        key = self._make_key(key, version=version)
    122122        self._lock.writer_enters()
    123123        try:
    124124            self._delete(key)
  • django/core/cache/backends/memcached.py

    diff -r 9aed8047e568 django/core/cache/backends/memcached.py
    a b  
    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='', version=1, key_func=None):
     22        BaseCache.__init__(self, params, key_prefix, version, key_func)
    2423        self._cache = memcache.Client(server.split(';'))
    2524
    2625    def _get_memcache_timeout(self, timeout):
     
    3938            timeout += int(time.time())
    4039        return timeout
    4140
    42     def add(self, key, value, timeout=0):
     41    def add(self, key, value, timeout=0, version=None):
     42        key = self._make_key(key, version=version)
    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
    47     def get(self, key, default=None):
    48         val = self._cache.get(smart_str(key))
     47    def get(self, key, default=None, version=None):
     48        key = self._make_key(key, version=version)
     49        val = self._cache.get(key)
    4950        if val is None:
    5051            return default
    5152        return val
    5253
    53     def set(self, key, value, timeout=0):
    54         self._cache.set(smart_str(key), value, self._get_memcache_timeout(timeout))
     54    def set(self, key, value, timeout=0, version=None):
     55        key = self._make_key(key, version=version)
     56        self._cache.set(key, value, self._get_memcache_timeout(timeout))
    5557
    56     def delete(self, key):
    57         self._cache.delete(smart_str(key))
     58    def delete(self, key, version=None):
     59        key = self._make_key(key, version=version)
     60        self._cache.delete(key)
    5861
    59     def get_many(self, keys):
    60         return self._cache.get_multi(map(smart_str,keys))
     62    def get_many(self, keys, version=None):
     63        new_keys = map(lambda x: self._make_key(x, version=version), keys)
     64        ret = self._cache.get_multi(new_keys)
     65        if ret:
     66            _ = {}
     67            m = dict(zip(new_keys, keys))
     68            for k, v in ret.items():
     69                _[m[k]] = v
     70            ret = _
     71        return ret
    6172
    6273    def close(self, **kwargs):
    6374        self._cache.disconnect_all()
    6475
    65     def incr(self, key, delta=1):
     76    def incr(self, key, delta=1, version=None):
     77        key = self._make_key(key, version=version)
    6678        try:
    6779            val = self._cache.incr(key, delta)
    6880
     
    7688
    7789        return val
    7890
    79     def decr(self, key, delta=1):
     91    def decr(self, key, delta=1, version=None):
     92        key = self._make_key(key, version=version)
    8093        try:
    8194            val = self._cache.decr(key, delta)
    8295
     
    89102            raise ValueError("Key '%s' not found" % key)
    90103        return val
    91104
    92     def set_many(self, data, timeout=0):
     105    def set_many(self, data, timeout=0, version=None):
    93106        safe_data = {}
    94107        for key, value in data.items():
     108            key = self._make_key(key, version=version)
    95109            if isinstance(value, unicode):
    96110                value = value.encode('utf-8')
    97             safe_data[smart_str(key)] = value
     111            safe_data[key] = value
    98112        self._cache.set_multi(safe_data, self._get_memcache_timeout(timeout))
    99113
    100     def delete_many(self, keys):
    101         self._cache.delete_multi(map(smart_str, keys))
     114    def delete_many(self, keys, version=None):
     115        l = lambda x: self._make_key(x, version=version)
     116        self._cache.delete_multi(map(l, keys))
    102117
    103118    def clear(self):
    104119        self._cache.flush_all()
  • docs/ref/settings.txt

    diff -r 9aed8047e568 docs/ref/settings.txt
    a b  
    136136
    137137The cache backend to use. See :doc:`/topics/cache`.
    138138
     139.. setting:: CACHE_KEY_FUNCTION
     140
     141CACHE_KEY_FUNCTION
     142------------------
     143
     144Default: ``None``
     145
     146A string containing a dotted path to a function that defines how to
     147compose a prefix, version and key into a final cache key. The default
     148implementation is equivalent to the function::
     149
     150    def make_key(key, key_prefix, version):
     151        return ':'.join([key_prefix, str(version), smart_str(key)])
     152
     153You may use any key function you want, as long as it adheres to the same
     154prototype.
     155
     156See the :ref:`cache documentation <cache_key_transformation>` for more information.
     157
    139158.. setting:: CACHE_MIDDLEWARE_ANONYMOUS_ONLY
    140159
    141160CACHE_MIDDLEWARE_ANONYMOUS_ONLY
     
    172191The default number of seconds to cache a page when the caching middleware or
    173192``cache_page()`` decorator is used.
    174193
     194.. setting:: CACHE_PREFIX
     195
     196CACHE_PREFIX
     197------------
     198
     199Default: ``''`` (Empty string)
     200
     201A string that will be automatically included (prepended by default) to
     202all cache keys used by the Django server.
     203
     204See the :ref:`cache documentation <cache_key_prefixing>` for more information.
     205
     206.. setting:: CACHE_VERSION
     207
     208CACHE_VERSION
     209-------------
     210
     211Default: ``1``
     212
     213The default version number for cache keys generated by the Django server.
     214
     215See the :ref:`cache documentation <cache_versioning>` for more information.
     216
     217
    175218.. setting:: CSRF_COOKIE_DOMAIN
    176219
    177220CSRF_COOKIE_DOMAIN
  • docs/releases/1.3.txt

    diff -r 9aed8047e568 docs/releases/1.3.txt
    a b  
    155155      :meth:`~django.test.client.Client.assertNumQueries` -- making it
    156156      easier to test the database activity associated with a view.
    157157
     158    * :ref:`Versioning <cache_versioning>`, :ref:`site-wide prefixing
     159      <cache_key_prefixing>` and :ref:`transformation
     160      <cache_key_transformation>` has been added to the cache API.
    158161
    159162.. _backwards-incompatible-changes-1.3:
    160163
  • docs/topics/cache.txt

    diff -r 9aed8047e568 docs/topics/cache.txt
    a b  
    643643    However, if the backend doesn't natively provide an increment/decrement
    644644    operation, it will be implemented using a two-step retrieve/update.
    645645
     646.. _cache_key_prefixing:
     647
     648Cache key prefixing
     649-------------------
     650
     651.. versionadded:: 1.3
     652
     653If you are sharing a cache instance between servers, or between your
     654production and development environments, it's possible for data cached
     655by one server to be used by another server. If the format of cached
     656data is different between servers, this can lead to some very hard to
     657diagnose problems.
     658
     659To prevent this, Django provides the ability to prefix all cache keys
     660used by a server. When a particular cache key is saved or retrieved,
     661Django will automatically prefix the cache key with the value of the
     662:setting:`CACHE_KEY_PREFIX` setting.
     663
     664By ensuring each Django instance has a different
     665:setting:`CACHE_KEY_PREFIX`, you can ensure that there will be no
     666collisions in cache values.
     667
     668.. _cache_versioning:
     669
     670Cache versioning
     671----------------
     672
     673.. versionadded:: 1.3
     674
     675When you change running code that uses cached values, you may need to
     676purge any existing cached values. The easiest way to do this is to
     677flush the entire cache, but this may result in the loss of cache
     678values that are still valid and useful, but are lost because the
     679entire cache is purged.
     680
     681Django provides a better way to target individual cache values.
     682Django's cache framework has a system-wide version identifier,
     683specified using the :setting:`CACHE_VERSION` setting. The value of
     684this setting is automatically combined with the cache prefix and the
     685user-provided cache key to obtain the final cache key.
     686
     687By default, any key request will automatically include the site
     688default cache key version. However, the primitive cache functions all
     689include a ``version`` argument, so you can specify a particular cache
     690key version to set or get. For example::
     691
     692    # Set version 2 of a cache key
     693    >>> cache.set('my_key', 'hello world!', version=2)
     694    # Get the default version (assuming version=1)
     695    >>> cache.get('my_key')
     696    None
     697    # Get version 2 of the same key
     698    >>> cache.get('my_key', version=2)
     699    'hello world!'
     700
     701The value of a specific key can be incremented and decremented using
     702the :func:`incr_version()` and :func:`decr_version()` methods. This
     703enables specific keys to be bumped to a new version, leaving other
     704keys unaffected. Continuing our previous example::
     705
     706    # Increment the version of 'my_key'
     707    >>> cache.incr_version('my_key')
     708    # The default version still isn't available
     709    >>> cache.get('my_key')
     710    None
     711    # Version 2 isn't available, either
     712    >>> cache.get('my_key', version=2)
     713    None
     714    # But version 3 *is* availble
     715    >>> cache.get('my_key', version=3)
     716    'hello world!'
     717
     718.. _cache_key_transformation:
     719
     720Cache key transformation
     721------------------------
     722
     723.. versionadded:: 1.3
     724
     725As described in the previous two sections, the cache key provided by a
     726user is not used verbatim -- it is combined with the cache prefix and
     727key version to provide a final cache key. By default, the three parts
     728are joined using colons to produce a final string::
     729
     730    def make_key(key, key_prefix, version):
     731        return ':'.join([key_prefix, str(version), smart_str(key)])
     732
     733If you want to combine the parts in different ways, or apply other
     734processing to the final key (e.g., taking a hash digest of the key
     735parts), you can provide a custom key function.
     736
     737The setting :setting:`CACHE_KEY_FUNCTION` specifies a dotted-path to
     738a function matching the prototype of :func:`make_key()` above. If
     739provided, this custom key function will be used instead of the default
     740key combining function.
     741
    646742Cache key warnings
    647743------------------
    648744
  • tests/regressiontests/cache/tests.py

    diff -r 9aed8047e568 tests/regressiontests/cache/tests.py
    a b  
    145145        "clear does nothing for the dummy cache backend"
    146146        self.cache.clear()
    147147
     148    def test_incr_version(self):
     149        "Dummy cache versions can't be incremented"
     150        self.cache.set('answer', 42)
     151        self.assertRaises(ValueError, self.cache.incr_version, 'answer')
     152        self.assertRaises(ValueError, self.cache.incr_version, 'does_not_exist')
     153
     154    def test_decr_version(self):
     155        "Dummy cache versions can't be decremented"
     156        self.cache.set('answer', 42)
     157        self.assertRaises(ValueError, self.cache.decr_version, 'answer')
     158        self.assertRaises(ValueError, self.cache.decr_version, 'does_not_exist')
     159
    148160
    149161class BaseCacheTests(object):
    150162    # A common set of tests to apply to all cache backends
    151     def tearDown(self):
    152         self.cache.clear()
    153163
    154164    def test_simple(self):
    155165        # Simple cache set/get works
     
    163173        self.assertEqual(result, False)
    164174        self.assertEqual(self.cache.get("addkey1"), "value")
    165175
     176    def test_prefix(self):
     177        # Test for same cache key conflicts between shared backend
     178        self.cache.set('somekey', 'value')
     179
     180        # should not be set in the prefixed cache
     181        self.assertFalse(self.prefix_cache.has_key('somekey'))
     182
     183        self.prefix_cache.set('somekey', 'value2')
     184
     185        self.assertEqual(self.cache.get('somekey'), 'value')
     186        self.assertEqual(self.prefix_cache.get('somekey'), 'value2')
     187
    166188    def test_non_existent(self):
    167189        # Non-existent cache keys return as None/default
    168190        # get with non-existent keys
     
    376398        with more liberal key rules. Refs #6447.
    377399
    378400        """
     401        # mimic custom ``make_key`` method being defined since the default will
     402        # never show the below warnings
     403        def func(key, *args):
     404            return key
     405
     406        old_func = self.cache.key_func
     407        self.cache.key_func = func
    379408        # On Python 2.6+ we could use the catch_warnings context
    380409        # manager to test this warning nicely. Since we can't do that
    381410        # yet, the cleanest option is to temporarily ask for
     
    390419            self.assertRaises(CacheKeyWarning, self.cache.set, 'a' * 251, 'value')
    391420        finally:
    392421            restore_warnings_state(_warnings_state)
     422            self.cache.key_func = old_func
     423
     424    def test_cache_versioning_get_set(self):
     425        # set, using default version = 1
     426        self.cache.set('answer1', 42)
     427        self.assertEqual(self.cache.get('answer1'), 42)
     428        self.assertEqual(self.cache.get('answer1', version=1), 42)
     429        self.assertEqual(self.cache.get('answer1', version=2), None)
     430
     431        self.assertEqual(self.v2_cache.get('answer1'), None)
     432        # print '---'
     433        # print 'c1',self.cache._cache
     434        # print 'v2',self.v2_cache._cache
     435        self.assertEqual(self.v2_cache.get('answer1', version=1), 42)
     436        self.assertEqual(self.v2_cache.get('answer1', version=2), None)
     437
     438        # set, default version = 1, but manually override version = 2
     439        self.cache.set('answer2', 42, version=2)
     440        self.assertEqual(self.cache.get('answer2'), None)
     441        self.assertEqual(self.cache.get('answer2', version=1), None)
     442        self.assertEqual(self.cache.get('answer2', version=2), 42)
     443
     444        self.assertEqual(self.v2_cache.get('answer2'), 42)
     445        self.assertEqual(self.v2_cache.get('answer2', version=1), None)
     446        self.assertEqual(self.v2_cache.get('answer2', version=2), 42)
     447
     448        # v2 set, using default version = 2
     449        self.v2_cache.set('answer3', 42)
     450        self.assertEqual(self.cache.get('answer3'), None)
     451        self.assertEqual(self.cache.get('answer3', version=1), None)
     452        self.assertEqual(self.cache.get('answer3', version=2), 42)
     453
     454        self.assertEqual(self.v2_cache.get('answer3'), 42)
     455        self.assertEqual(self.v2_cache.get('answer3', version=1), None)
     456        self.assertEqual(self.v2_cache.get('answer3', version=2), 42)
     457
     458        # v2 set, default version = 2, but manually override version = 1
     459        self.v2_cache.set('answer4', 42, version=1)
     460        self.assertEqual(self.cache.get('answer4'), 42)
     461        self.assertEqual(self.cache.get('answer4', version=1), 42)
     462        self.assertEqual(self.cache.get('answer4', version=2), None)
     463
     464        self.assertEqual(self.v2_cache.get('answer4'), None)
     465        self.assertEqual(self.v2_cache.get('answer4', version=1), 42)
     466        self.assertEqual(self.v2_cache.get('answer4', version=2), None)
     467
     468    def test_cache_versioning_has_key(self):
     469        self.cache.set('answer1', 42)
     470
     471        # has_key
     472        self.assertTrue(self.cache.has_key('answer1'))
     473        self.assertTrue(self.cache.has_key('answer1', version=1))
     474        self.assertFalse(self.cache.has_key('answer1', version=2))
     475
     476        self.assertFalse(self.v2_cache.has_key('answer1'))
     477        self.assertTrue(self.v2_cache.has_key('answer1', version=1))
     478        self.assertFalse(self.v2_cache.has_key('answer1', version=2))
     479
     480    def test_cache_versioning_delete(self):
     481        self.cache.set('answer1', 37, version=1)
     482        self.cache.set('answer1', 42, version=2)
     483        self.cache.delete('answer1')
     484        self.assertEqual(self.cache.get('answer1', version=1), None)
     485        self.assertEqual(self.cache.get('answer1', version=2), 42)
     486
     487        self.cache.set('answer2', 37, version=1)
     488        self.cache.set('answer2', 42, version=2)
     489        self.cache.delete('answer2', version=2)
     490        self.assertEqual(self.cache.get('answer2', version=1), 37)
     491        self.assertEqual(self.cache.get('answer2', version=2), None)
     492
     493        self.cache.set('answer3', 37, version=1)
     494        self.cache.set('answer3', 42, version=2)
     495        self.v2_cache.delete('answer3')
     496        self.assertEqual(self.cache.get('answer3', version=1), 37)
     497        self.assertEqual(self.cache.get('answer3', version=2), None)
     498
     499        self.cache.set('answer4', 37, version=1)
     500        self.cache.set('answer4', 42, version=2)
     501        self.v2_cache.delete('answer4', version=1)
     502        self.assertEqual(self.cache.get('answer4', version=1), None)
     503        self.assertEqual(self.cache.get('answer4', version=2), 42)
     504
     505    def test_cache_versioning_incr_decr(self):
     506        self.cache.set('answer1', 37, version=1)
     507        self.cache.set('answer1', 42, version=2)
     508        self.cache.incr('answer1')
     509        self.assertEqual(self.cache.get('answer1', version=1), 38)
     510        self.assertEqual(self.cache.get('answer1', version=2), 42)
     511        self.cache.decr('answer1')
     512        self.assertEqual(self.cache.get('answer1', version=1), 37)
     513        self.assertEqual(self.cache.get('answer1', version=2), 42)
     514
     515        self.cache.set('answer2', 37, version=1)
     516        self.cache.set('answer2', 42, version=2)
     517        self.cache.incr('answer2', version=2)
     518        self.assertEqual(self.cache.get('answer2', version=1), 37)
     519        self.assertEqual(self.cache.get('answer2', version=2), 43)
     520        self.cache.decr('answer2', version=2)
     521        self.assertEqual(self.cache.get('answer2', version=1), 37)
     522        self.assertEqual(self.cache.get('answer2', version=2), 42)
     523
     524        self.cache.set('answer3', 37, version=1)
     525        self.cache.set('answer3', 42, version=2)
     526        self.v2_cache.incr('answer3')
     527        self.assertEqual(self.cache.get('answer3', version=1), 37)
     528        self.assertEqual(self.cache.get('answer3', version=2), 43)
     529        self.v2_cache.decr('answer3')
     530        self.assertEqual(self.cache.get('answer3', version=1), 37)
     531        self.assertEqual(self.cache.get('answer3', version=2), 42)
     532
     533        self.cache.set('answer4', 37, version=1)
     534        self.cache.set('answer4', 42, version=2)
     535        self.v2_cache.incr('answer4', version=1)
     536        self.assertEqual(self.cache.get('answer4', version=1), 38)
     537        self.assertEqual(self.cache.get('answer4', version=2), 42)
     538        self.v2_cache.decr('answer4', version=1)
     539        self.assertEqual(self.cache.get('answer4', version=1), 37)
     540        self.assertEqual(self.cache.get('answer4', version=2), 42)
     541
     542    def test_cache_versioning_get_set_many(self):
     543        # set, using default version = 1
     544        self.cache.set_many({'ford1': 37, 'arthur1': 42})
     545        self.assertEqual(self.cache.get_many(['ford1','arthur1']),
     546                         {'ford1': 37, 'arthur1': 42})
     547        self.assertEqual(self.cache.get_many(['ford1','arthur1'], version=1),
     548                         {'ford1': 37, 'arthur1': 42})
     549        self.assertEqual(self.cache.get_many(['ford1','arthur1'], version=2), {})
     550
     551        self.assertEqual(self.v2_cache.get_many(['ford1','arthur1']), {})
     552        self.assertEqual(self.v2_cache.get_many(['ford1','arthur1'], version=1),
     553                         {'ford1': 37, 'arthur1': 42})
     554        self.assertEqual(self.v2_cache.get_many(['ford1','arthur1'], version=2), {})
     555
     556        # set, default version = 1, but manually override version = 2
     557        self.cache.set_many({'ford2': 37, 'arthur2': 42}, version=2)
     558        self.assertEqual(self.cache.get_many(['ford2','arthur2']), {})
     559        self.assertEqual(self.cache.get_many(['ford2','arthur2'], version=1), {})
     560        self.assertEqual(self.cache.get_many(['ford2','arthur2'], version=2),
     561                         {'ford2': 37, 'arthur2': 42})
     562
     563        self.assertEqual(self.v2_cache.get_many(['ford2','arthur2']),
     564                         {'ford2': 37, 'arthur2': 42})
     565        self.assertEqual(self.v2_cache.get_many(['ford2','arthur2'], version=1), {})
     566        self.assertEqual(self.v2_cache.get_many(['ford2','arthur2'], version=2),
     567                         {'ford2': 37, 'arthur2': 42})
     568
     569        # v2 set, using default version = 2
     570        self.v2_cache.set_many({'ford3': 37, 'arthur3': 42})
     571        self.assertEqual(self.cache.get_many(['ford3','arthur3']), {})
     572        self.assertEqual(self.cache.get_many(['ford3','arthur3'], version=1), {})
     573        self.assertEqual(self.cache.get_many(['ford3','arthur3'], version=2),
     574                         {'ford3': 37, 'arthur3': 42})
     575
     576        self.assertEqual(self.v2_cache.get_many(['ford3','arthur3']),
     577                         {'ford3': 37, 'arthur3': 42})
     578        self.assertEqual(self.v2_cache.get_many(['ford3','arthur3'], version=1), {})
     579        self.assertEqual(self.v2_cache.get_many(['ford3','arthur3'], version=2),
     580                         {'ford3': 37, 'arthur3': 42})
     581
     582        # v2 set, default version = 2, but manually override version = 1
     583        self.v2_cache.set_many({'ford4': 37, 'arthur4': 42}, version=1)
     584        self.assertEqual(self.cache.get_many(['ford4','arthur4']),
     585                         {'ford4': 37, 'arthur4': 42})
     586        self.assertEqual(self.cache.get_many(['ford4','arthur4'], version=1),
     587                         {'ford4': 37, 'arthur4': 42})
     588        self.assertEqual(self.cache.get_many(['ford4','arthur4'], version=2), {})
     589
     590        self.assertEqual(self.v2_cache.get_many(['ford4','arthur4']), {})
     591        self.assertEqual(self.v2_cache.get_many(['ford4','arthur4'], version=1),
     592                         {'ford4': 37, 'arthur4': 42})
     593        self.assertEqual(self.v2_cache.get_many(['ford4','arthur4'], version=2), {})
     594
     595    def test_incr_version(self):
     596        self.cache.set('answer', 42, version=2)
     597        self.assertEqual(self.cache.get('answer'), None)
     598        self.assertEqual(self.cache.get('answer', version=1), None)
     599        self.assertEqual(self.cache.get('answer', version=2), 42)
     600        self.assertEqual(self.cache.get('answer', version=3), None)
     601
     602        self.assertEqual(self.cache.incr_version('answer', version=2), 3)
     603        self.assertEqual(self.cache.get('answer'), None)
     604        self.assertEqual(self.cache.get('answer', version=1), None)
     605        self.assertEqual(self.cache.get('answer', version=2), None)
     606        self.assertEqual(self.cache.get('answer', version=3), 42)
     607
     608        self.v2_cache.set('answer2', 42)
     609        self.assertEqual(self.v2_cache.get('answer2'), 42)
     610        self.assertEqual(self.v2_cache.get('answer2', version=1), None)
     611        self.assertEqual(self.v2_cache.get('answer2', version=2), 42)
     612        self.assertEqual(self.v2_cache.get('answer2', version=3), None)
     613
     614        self.assertEqual(self.v2_cache.incr_version('answer2'), 3)
     615        self.assertEqual(self.v2_cache.get('answer2'), None)
     616        self.assertEqual(self.v2_cache.get('answer2', version=1), None)
     617        self.assertEqual(self.v2_cache.get('answer2', version=2), None)
     618        self.assertEqual(self.v2_cache.get('answer2', version=3), 42)
     619
     620        self.assertRaises(ValueError, self.cache.incr_version, 'does_not_exist')
     621
     622    def test_decr_version(self):
     623        self.cache.set('answer', 42, version=2)
     624        self.assertEqual(self.cache.get('answer'), None)
     625        self.assertEqual(self.cache.get('answer', version=1), None)
     626        self.assertEqual(self.cache.get('answer', version=2), 42)
     627
     628        self.assertEqual(self.cache.decr_version('answer', version=2), 1)
     629        self.assertEqual(self.cache.get('answer'), 42)
     630        self.assertEqual(self.cache.get('answer', version=1), 42)
     631        self.assertEqual(self.cache.get('answer', version=2), None)
     632
     633        self.v2_cache.set('answer2', 42)
     634        self.assertEqual(self.v2_cache.get('answer2'), 42)
     635        self.assertEqual(self.v2_cache.get('answer2', version=1), None)
     636        self.assertEqual(self.v2_cache.get('answer2', version=2), 42)
     637
     638        self.assertEqual(self.v2_cache.decr_version('answer2'), 1)
     639        self.assertEqual(self.v2_cache.get('answer2'), None)
     640        self.assertEqual(self.v2_cache.get('answer2', version=1), 42)
     641        self.assertEqual(self.v2_cache.get('answer2', version=2), None)
     642
     643        self.assertRaises(ValueError, self.cache.decr_version, 'does_not_exist', version=2)
     644
     645    def test_custom_key_func(self):
     646        # Two caches with different key functions aren't visible to each other
     647        self.cache.set('answer1', 42)
     648        self.assertEqual(self.cache.get('answer1'), 42)
     649        self.assertEqual(self.custom_key_cache.get('answer1'), None)
     650
     651        self.custom_key_cache.set('answer2', 42)
     652        self.assertEqual(self.cache.get('answer2'), None)
     653        self.assertEqual(self.custom_key_cache.get('answer2'), 42)
     654
     655def custom_key_func(key, key_prefix, version):
     656    "A customized cache key function"
     657    return 'CUSTOM-' + '-'.join([key_prefix, str(version), key])
    393658
    394659class DBCacheTests(unittest.TestCase, BaseCacheTests):
    395660    def setUp(self):
     
    397662        self._table_name = 'test cache table'
    398663        management.call_command('createcachetable', self._table_name, verbosity=0, interactive=False)
    399664        self.cache = get_cache('db://%s?max_entries=30' % self._table_name)
     665        self.prefix_cache = get_cache('db://%s' % self._table_name, key_prefix='cacheprefix')
     666        self.v2_cache = get_cache('db://%s' % self._table_name, version=2)
     667        self.custom_key_cache = get_cache('db://%s' % self._table_name, key_func=custom_key_func)
    400668
    401669    def tearDown(self):
    402670        from django.db import connection
     
    413681class LocMemCacheTests(unittest.TestCase, BaseCacheTests):
    414682    def setUp(self):
    415683        self.cache = get_cache('locmem://?max_entries=30')
     684        self.prefix_cache = get_cache('locmem://', key_prefix='cacheprefix')
     685        self.v2_cache = get_cache('locmem://', version=2)
     686        self.custom_key_cache = get_cache('locmem://?max_entries=30', key_func=custom_key_func)
     687
     688        # LocMem requires a hack to make the other caches
     689        # share a data store with the 'normal' cache.
     690        self.prefix_cache._cache = self.cache._cache
     691        self.prefix_cache._expire_info = self.cache._expire_info
     692
     693        self.v2_cache._cache = self.cache._cache
     694        self.v2_cache._expire_info = self.cache._expire_info
     695
     696        self.custom_key_cache._cache = self.cache._cache
     697        self.custom_key_cache._expire_info = self.cache._expire_info
    416698
    417699    def test_cull(self):
    418700        self.perform_cull_test(50, 29)
     
    428710class MemcachedCacheTests(unittest.TestCase, BaseCacheTests):
    429711    def setUp(self):
    430712        self.cache = get_cache(settings.CACHE_BACKEND)
     713        self.prefix_cache = get_cache(settings.CACHE_BACKEND, key_prefix='cacheprefix')
     714        self.v2_cache = get_cache(settings.CACHE_BACKEND, version=2)
     715        self.custom_key_cache = get_cache(settings.CACHE_BACKEND, key_func=custom_key_func)
     716
     717    def tearDown(self):
     718        self.cache.clear()
     719        self.prefix_cache.clear()
     720        self.v2_cache.clear()
     721        self.custom_key_cache.clear()
    431722
    432723    def test_invalid_keys(self):
    433724        """
     
    439730        that a generic exception of some kind is raised.
    440731
    441732        """
     733        _warnings_state = get_warnings_state()
     734        warnings.simplefilter("ignore", CacheKeyWarning)
     735
    442736        # memcached does not allow whitespace or control characters in keys
    443737        self.assertRaises(Exception, self.cache.set, 'key with spaces', 'value')
    444738        # memcached limits key length to 250
    445739        self.assertRaises(Exception, self.cache.set, 'a' * 251, 'value')
     740
     741        restore_warnings_state(_warnings_state)
     742
    446743MemcachedCacheTests = unittest.skipUnless(settings.CACHE_BACKEND.startswith('memcached://'), "memcached not available")(MemcachedCacheTests)
    447744
    448745class FileBasedCacheTests(unittest.TestCase, BaseCacheTests):
     
    452749    def setUp(self):
    453750        self.dirname = tempfile.mkdtemp()
    454751        self.cache = get_cache('file://%s?max_entries=30' % self.dirname)
     752        self.prefix_cache = get_cache('file://%s' % self.dirname, key_prefix='cacheprefix')
     753        self.v2_cache = get_cache('file://%s' % self.dirname, version=2)
     754        self.custom_key_cache = get_cache('file://%s' % self.dirname, key_func=custom_key_func)
     755
     756    def tearDown(self):
     757        self.cache.clear()
     758        self.prefix_cache.clear()
     759        self.v2_cache.clear()
     760        self.custom_key_cache.clear()
    455761
    456762    def test_hashing(self):
    457763        """Test that keys are hashed into subdirectories correctly"""
    458764        self.cache.set("foo", "bar")
    459         keyhash = md5_constructor("foo").hexdigest()
     765        key = self.cache._make_key("foo")
     766        keyhash = md5_constructor(key).hexdigest()
    460767        keypath = os.path.join(self.dirname, keyhash[:2], keyhash[2:4], keyhash[4:])
    461768        self.assert_(os.path.exists(keypath))
    462769
     
    465772        Make sure that the created subdirectories are correctly removed when empty.
    466773        """
    467774        self.cache.set("foo", "bar")
    468         keyhash = md5_constructor("foo").hexdigest()
     775        key = self.cache._make_key("foo")
     776        keyhash = md5_constructor(key).hexdigest()
    469777        keypath = os.path.join(self.dirname, keyhash[:2], keyhash[2:4], keyhash[4:])
    470778        self.assert_(os.path.exists(keypath))
    471779
     
    475783        self.assert_(not os.path.exists(os.path.dirname(os.path.dirname(keypath))))
    476784
    477785    def test_cull(self):
    478         self.perform_cull_test(50, 28)
     786        self.perform_cull_test(50, 29)
    479787
    480788class CustomCacheKeyValidationTests(unittest.TestCase):
    481789    """
     
    498806
    499807    def setUp(self):
    500808        self.path = '/cache/test/'
    501         self.old_settings_key_prefix = settings.CACHE_MIDDLEWARE_KEY_PREFIX
    502         self.old_middleware_seconds = settings.CACHE_MIDDLEWARE_SECONDS
     809        self.old_cache_middleware_key_prefix = settings.CACHE_MIDDLEWARE_KEY_PREFIX
     810        self.old_cache_middleware_seconds = settings.CACHE_MIDDLEWARE_SECONDS
    503811        self.orig_use_i18n = settings.USE_I18N
    504812        settings.CACHE_MIDDLEWARE_KEY_PREFIX = 'settingsprefix'
    505813        settings.CACHE_MIDDLEWARE_SECONDS = 1
    506814        settings.USE_I18N = False
    507815
    508816    def tearDown(self):
    509         settings.CACHE_MIDDLEWARE_KEY_PREFIX = self.old_settings_key_prefix
    510         settings.CACHE_MIDDLEWARE_SECONDS = self.old_middleware_seconds
     817        settings.CACHE_MIDDLEWARE_KEY_PREFIX = self.old_cache_middleware_key_prefix
     818        settings.CACHE_MIDDLEWARE_SECONDS = self.old_cache_middleware_seconds
    511819        settings.USE_I18N = self.orig_use_i18n
    512820
    513821    def _get_request(self, path, method='GET'):
     
    561869        learn_cache_key(request, response)
    562870        self.assertEqual(get_cache_key(request), 'views.decorators.cache.cache_page.settingsprefix.HEAD.a8c87a3d8c44853d7f79474f7ffe4ad5.d41d8cd98f00b204e9800998ecf8427e')
    563871
     872class PrefixedCacheUtils(CacheUtils):
     873    def setUp(self):
     874        super(PrefixedCacheUtils, self).setUp()
     875        self.old_cache_key_prefix = settings.CACHE_KEY_PREFIX
     876        settings.CACHE_KEY_PREFIX = 'cacheprefix'
     877
     878    def tearDown(self):
     879        super(PrefixedCacheUtils, self).tearDown()
     880        settings.CACHE_KEY_PREFIX = self.old_cache_key_prefix
     881
    564882class CacheHEADTest(unittest.TestCase):
    565883
    566884    def setUp(self):
     
    7141032        get_cache_data = FetchFromCacheMiddleware().process_request(request)
    7151033        self.assertEqual(get_cache_data.content, es_message)
    7161034
     1035class PrefixedCacheI18nTest(CacheI18nTest):
     1036    def setUp(self):
     1037        super(PrefixedCacheI18nTest, self).setUp()
     1038        self.old_cache_key_prefix = settings.CACHE_KEY_PREFIX
     1039        settings.CACHE_KEY_PREFIX = 'cacheprefix'
     1040
     1041    def tearDown(self):
     1042        super(PrefixedCacheI18nTest, self).tearDown()
     1043        settings.CACHE_KEY_PREFIX = self.old_cache_key_prefix
     1044
    7171045if __name__ == '__main__':
    7181046    unittest.main()
Back to Top