Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Fixed #11675 -- Added support for the PyLibMC cache library. In order…

… to support this, and clean up some other 1.3 caching additions, this patch also includes some changes to the way caches are defined. This means you can now have multiple caches, in the same way you have multiple databases. A huge thanks to Jacob Burch for the work on the PyLibMC backend, and to Jannis for his work on the cache definition changes.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@15005 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information...
commit 673e6fc7fb243ed44841b9969d26a161c25733b3 1 parent 3cf8502
Russell Keith-Magee authored December 21, 2010
7  django/conf/global_settings.py
@@ -431,14 +431,15 @@
431 431
 # CACHE #
432 432
 #########
433 433
 
  434
+# New format
  435
+CACHES = {
  436
+}
434 437
 # The cache backend to use.  See the docstring in django.core.cache for the
435 438
 # possible values.
436 439
 CACHE_BACKEND = 'locmem://'
437  
-CACHE_VERSION = 1
438  
-CACHE_KEY_PREFIX = ''
439  
-CACHE_KEY_FUNCTION = None
440 440
 CACHE_MIDDLEWARE_KEY_PREFIX = ''
441 441
 CACHE_MIDDLEWARE_SECONDS = 600
  442
+CACHE_MIDDLEWARE_ALIAS = 'default'
442 443
 
443 444
 ####################
444 445
 # COMMENTS         #
13  django/contrib/gis/db/backends/spatialite/creation.py
... ...
@@ -1,5 +1,7 @@
1 1
 import os
2 2
 from django.conf import settings
  3
+from django.core.cache import get_cache
  4
+from django.core.cache.backends.db import BaseDatabaseCache
3 5
 from django.core.exceptions import ImproperlyConfigured
4 6
 from django.core.management import call_command
5 7
 from django.db.backends.sqlite3.creation import DatabaseCreation
@@ -28,11 +30,12 @@ def create_test_db(self, verbosity=1, autoclobber=False):
28 30
         self.load_spatialite_sql()
29 31
         call_command('syncdb', verbosity=verbosity, interactive=False, database=self.connection.alias)
30 32
 
31  
-        if settings.CACHE_BACKEND.startswith('db://'):
32  
-            from django.core.cache import parse_backend_uri
33  
-            _, cache_name, _ = parse_backend_uri(settings.CACHE_BACKEND)
34  
-            call_command('createcachetable', cache_name)
35  
-
  33
+        for cache_alias in settings.CACHES:
  34
+            cache = get_cache(cache_alias)
  35
+            if isinstance(cache, BaseDatabaseCache):
  36
+                from django.db import router
  37
+                if router.allow_syncdb(self.connection.alias, cache.cache_model_class):
  38
+                    call_command('createcachetable', cache._table, database=self.connection.alias)
36 39
         # Get a cursor (even though we don't need one yet). This has
37 40
         # the side effect of initializing the test database.
38 41
         cursor = self.connection.cursor()
128  django/core/cache/__init__.py
@@ -12,8 +12,13 @@
12 12
 (e.g. "memcached://127.0.0.1:11211/") and returns an instance of a backend
13 13
 cache class.
14 14
 
15  
-See docs/cache.txt for information on the public API.
  15
+See docs/topics/cache.txt for information on the public API.
16 16
 """
  17
+from django.conf import settings
  18
+from django.core import signals
  19
+from django.core.cache.backends.base import (
  20
+    InvalidCacheBackendError, CacheKeyWarning, BaseCache)
  21
+from django.utils import importlib
17 22
 
18 23
 try:
19 24
     # The mod_python version is more efficient, so try importing it first.
@@ -27,10 +32,9 @@
27 32
         # PendingDeprecationWarning
28 33
         from cgi import parse_qsl
29 34
 
30  
-from django.conf import settings
31  
-from django.core import signals
32  
-from django.core.cache.backends.base import InvalidCacheBackendError, CacheKeyWarning
33  
-from django.utils import importlib
  35
+__all__ = [
  36
+    'get_cache', 'cache', 'DEFAULT_CACHE_ALIAS'
  37
+]
34 38
 
35 39
 # Name for use in settings file --> name of module in "backends" directory.
36 40
 # Any backend scheme that is not in this dictionary is treated as a Python
@@ -43,6 +47,8 @@
43 47
     'dummy': 'dummy',
44 48
 }
45 49
 
  50
+DEFAULT_CACHE_ALIAS = 'default'
  51
+
46 52
 def parse_backend_uri(backend_uri):
47 53
     """
48 54
     Converts the "backend_uri" into a cache scheme ('db', 'memcached', etc), a
@@ -67,32 +73,102 @@ def parse_backend_uri(backend_uri):
67 73
 
68 74
     return scheme, host, params
69 75
 
70  
-def 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 = key_func.rsplit('.', 1)
80  
-        key_func_module = importlib.import_module(key_func_module_path)
81  
-        key_func = getattr(key_func_module, key_func_name)
82  
-
83  
-    scheme, host, params = parse_backend_uri(backend_uri)
84  
-    if scheme in BACKENDS:
85  
-        name = 'django.core.cache.backends.%s' % BACKENDS[scheme]
  76
+if not settings.CACHES:
  77
+    import warnings
  78
+    warnings.warn(
  79
+        "settings.CACHE_* is deprecated; use settings.CACHES instead.",
  80
+        PendingDeprecationWarning
  81
+    )
  82
+    # Mapping for new-style cache backend api
  83
+    backend_classes = {
  84
+        'memcached': 'memcached.CacheClass',
  85
+        'locmem': 'locmem.LocMemCache',
  86
+        'file': 'filebased.FileBasedCache',
  87
+        'db': 'db.DatabaseCache',
  88
+        'dummy': 'dummy.DummyCache',
  89
+    }
  90
+    engine, host, params = parse_backend_uri(settings.CACHE_BACKEND)
  91
+    if engine in backend_classes:
  92
+        engine = 'django.core.cache.backends.%s' % backend_classes[engine]
  93
+    defaults = {
  94
+        'BACKEND': engine,
  95
+        'LOCATION': host,
  96
+    }
  97
+    defaults.update(params)
  98
+    settings.CACHES[DEFAULT_CACHE_ALIAS] = defaults
  99
+
  100
+if DEFAULT_CACHE_ALIAS not in settings.CACHES:
  101
+    raise ImproperlyConfigured("You must define a '%s' cache" % DEFAULT_CACHE_ALIAS)
  102
+
  103
+def parse_backend_conf(backend, **kwargs):
  104
+    """
  105
+    Helper function to parse the backend configuration
  106
+    that doesn't use the URI notation.
  107
+    """
  108
+    # Try to get the CACHES entry for the given backend name first
  109
+    conf = settings.CACHES.get(backend, None)
  110
+    if conf is not None:
  111
+        args = conf.copy()
  112
+        backend = args.pop('BACKEND')
  113
+        location = args.pop('LOCATION', '')
  114
+        return backend, location, args
86 115
     else:
87  
-        name = scheme
88  
-    module = importlib.import_module(name)
89  
-    return module.CacheClass(host, params, key_prefix=key_prefix, version=version, key_func=key_func)
  116
+        # Trying to import the given backend, in case it's a dotted path
  117
+        mod_path, cls_name = backend.rsplit('.', 1)
  118
+        try:
  119
+            mod = importlib.import_module(mod_path)
  120
+            backend_cls = getattr(mod, cls_name)
  121
+        except (AttributeError, ImportError):
  122
+            raise InvalidCacheBackendError("Could not find backend '%s'" % backend)
  123
+        location = kwargs.pop('LOCATION', '')
  124
+        return backend, location, kwargs
  125
+    raise InvalidCacheBackendError(
  126
+        "Couldn't find a cache backend named '%s'" % backend)
90 127
 
91  
-cache = get_cache(settings.CACHE_BACKEND)
  128
+def get_cache(backend, **kwargs):
  129
+    """
  130
+    Function to load a cache backend dynamically. This is flexible by design
  131
+    to allow different use cases:
  132
+
  133
+    To load a backend with the old URI-based notation::
  134
+
  135
+        cache = get_cache('locmem://')
  136
+
  137
+    To load a backend that is pre-defined in the settings::
  138
+
  139
+        cache = get_cache('default')
  140
+
  141
+    To load a backend with its dotted import path,
  142
+    including arbitrary options::
  143
+
  144
+        cache = get_cache('django.core.cache.backends.memcached.MemcachedCache', **{
  145
+            'LOCATION': '127.0.0.1:11211', 'TIMEOUT': 30,
  146
+        })
  147
+
  148
+    """
  149
+    try:
  150
+        if '://' in backend:
  151
+            # for backwards compatibility
  152
+            backend, location, params = parse_backend_uri(backend)
  153
+            if backend in BACKENDS:
  154
+                backend = 'django.core.cache.backends.%s' % BACKENDS[backend]
  155
+            params.update(kwargs)
  156
+            mod = importlib.import_module(backend)
  157
+            backend_cls = mod.CacheClass
  158
+        else:
  159
+            backend, location, params = parse_backend_conf(backend, **kwargs)
  160
+            mod_path, cls_name = backend.rsplit('.', 1)
  161
+            mod = importlib.import_module(mod_path)
  162
+            backend_cls = getattr(mod, cls_name)
  163
+    except (AttributeError, ImportError), e:
  164
+        raise InvalidCacheBackendError(
  165
+            "Could not find backend '%s': %s" % (backend, e))
  166
+    return backend_cls(location, params)
  167
+
  168
+cache = get_cache(DEFAULT_CACHE_ALIAS)
92 169
 
93 170
 # Some caches -- python-memcached in particular -- need to do a cleanup at the
94 171
 # end of a request cycle. If the cache provides a close() method, wire it up
95 172
 # here.
96 173
 if hasattr(cache, 'close'):
97 174
     signals.request_finished.connect(cache.close)
98  
-
37  django/core/cache/backends/base.py
@@ -2,8 +2,10 @@
2 2
 
3 3
 import warnings
4 4
 
  5
+from django.conf import settings
5 6
 from django.core.exceptions import ImproperlyConfigured, DjangoRuntimeWarning
6 7
 from django.utils.encoding import smart_str
  8
+from django.utils.importlib import import_module
7 9
 
8 10
 class InvalidCacheBackendError(ImproperlyConfigured):
9 11
     pass
@@ -15,38 +17,55 @@ class CacheKeyWarning(DjangoRuntimeWarning):
15 17
 MEMCACHE_MAX_KEY_LENGTH = 250
16 18
 
17 19
 def default_key_func(key, key_prefix, version):
18  
-    """Default function to generate keys.
  20
+    """
  21
+    Default function to generate keys.
19 22
 
20 23
     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
  24
+    the `key_prefix'. KEY_FUNCTION can be used to specify an alternate
22 25
     function with custom key making behavior.
23 26
     """
24 27
     return ':'.join([key_prefix, str(version), smart_str(key)])
25 28
 
  29
+def get_key_func(key_func):
  30
+    """
  31
+    Function to decide which key function to use.
  32
+
  33
+    Defaults to ``default_key_func``.
  34
+    """
  35
+    if key_func is not None:
  36
+        if callable(key_func):
  37
+            return key_func
  38
+        else:
  39
+            key_func_module_path, key_func_name = key_func.rsplit('.', 1)
  40
+            key_func_module = import_module(key_func_module_path)
  41
+            return getattr(key_func_module, key_func_name)
  42
+    return default_key_func
  43
+
26 44
 class BaseCache(object):
27  
-    def __init__(self, params, key_prefix='', version=1, key_func=None):
28  
-        timeout = params.get('timeout', 300)
  45
+    def __init__(self, params):
  46
+        timeout = params.get('timeout', params.get('TIMEOUT', 300))
29 47
         try:
30 48
             timeout = int(timeout)
31 49
         except (ValueError, TypeError):
32 50
             timeout = 300
33 51
         self.default_timeout = timeout
34 52
 
35  
-        max_entries = params.get('max_entries', 300)
  53
+        options = params.get('OPTIONS', {})
  54
+        max_entries = params.get('max_entries', options.get('MAX_ENTRIES', 300))
36 55
         try:
37 56
             self._max_entries = int(max_entries)
38 57
         except (ValueError, TypeError):
39 58
             self._max_entries = 300
40 59
 
41  
-        cull_frequency = params.get('cull_frequency', 3)
  60
+        cull_frequency = params.get('cull_frequency', options.get('CULL_FREQUENCY', 3))
42 61
         try:
43 62
             self._cull_frequency = int(cull_frequency)
44 63
         except (ValueError, TypeError):
45 64
             self._cull_frequency = 3
46 65
 
47  
-        self.key_prefix = smart_str(key_prefix)
48  
-        self.version = version
49  
-        self.key_func = key_func or default_key_func
  66
+        self.key_prefix = smart_str(params.get('KEY_PREFIX', ''))
  67
+        self.version = params.get('VERSION', 1)
  68
+        self.key_func = get_key_func(params.get('KEY_FUNCTION', None))
50 69
 
51 70
     def make_key(self, key, version=None):
52 71
         """Constructs the key used by all other methods. By default it
12  django/core/cache/backends/db.py
@@ -25,16 +25,16 @@ def __init__(self, table):
25 25
         self.managed = True
26 26
         self.proxy = False
27 27
 
28  
-class BaseDatabaseCacheClass(BaseCache):
29  
-    def __init__(self, table, params, key_prefix='', version=1, key_func=None):
30  
-        BaseCache.__init__(self, params, key_prefix, version, key_func)
  28
+class BaseDatabaseCache(BaseCache):
  29
+    def __init__(self, table, params):
  30
+        BaseCache.__init__(self, params)
31 31
         self._table = table
32 32
 
33 33
         class CacheEntry(object):
34 34
             _meta = Options(table)
35 35
         self.cache_model_class = CacheEntry
36 36
 
37  
-class CacheClass(BaseDatabaseCacheClass):
  37
+class DatabaseCache(BaseDatabaseCache):
38 38
     def get(self, key, default=None, version=None):
39 39
         key = self.make_key(key, version=version)
40 40
         self.validate_key(key)
@@ -140,3 +140,7 @@ def clear(self):
140 140
         table = connections[db].ops.quote_name(self._table)
141 141
         cursor = connections[db].cursor()
142 142
         cursor.execute('DELETE FROM %s' % table)
  143
+
  144
+# For backwards compatibility
  145
+class CacheClass(DatabaseCache):
  146
+    pass
6  django/core/cache/backends/dummy.py
@@ -2,7 +2,7 @@
2 2
 
3 3
 from django.core.cache.backends.base import BaseCache
4 4
 
5  
-class CacheClass(BaseCache):
  5
+class DummyCache(BaseCache):
6 6
     def __init__(self, host, *args, **kwargs):
7 7
         BaseCache.__init__(self, *args, **kwargs)
8 8
 
@@ -40,3 +40,7 @@ def delete_many(self, keys, version=None):
40 40
 
41 41
     def clear(self):
42 42
         pass
  43
+
  44
+# For backwards compatibility
  45
+class CacheClass(DummyCache):
  46
+    pass
10  django/core/cache/backends/filebased.py
@@ -11,9 +11,9 @@
11 11
 from django.core.cache.backends.base import BaseCache
12 12
 from django.utils.hashcompat import md5_constructor
13 13
 
14  
-class CacheClass(BaseCache):
15  
-    def __init__(self, dir, params, key_prefix='', version=1, key_func=None):
16  
-        BaseCache.__init__(self, params, key_prefix, version, key_func)
  14
+class FileBasedCache(BaseCache):
  15
+    def __init__(self, dir, params):
  16
+        BaseCache.__init__(self, params)
17 17
         self._dir = dir
18 18
         if not os.path.exists(self._dir):
19 19
             self._createdir()
@@ -161,3 +161,7 @@ def clear(self):
161 161
             shutil.rmtree(self._dir)
162 162
         except (IOError, OSError):
163 163
             pass
  164
+
  165
+# For backwards compatibility
  166
+class CacheClass(FileBasedCache):
  167
+    pass
23  django/core/cache/backends/locmem.py
@@ -9,12 +9,19 @@
9 9
 from django.core.cache.backends.base import BaseCache
10 10
 from django.utils.synch import RWLock
11 11
 
12  
-class CacheClass(BaseCache):
13  
-    def __init__(self, _, params, key_prefix='', version=1, key_func=None):
14  
-        BaseCache.__init__(self, params, key_prefix, version, key_func)
15  
-        self._cache = {}
16  
-        self._expire_info = {}
17  
-        self._lock = RWLock()
  12
+# Global in-memory store of cache data. Keyed by name, to provide
  13
+# multiple named local memory caches.
  14
+_caches = {}
  15
+_expire_info = {}
  16
+_locks = {}
  17
+
  18
+class LocMemCache(BaseCache):
  19
+    def __init__(self, name, params):
  20
+        BaseCache.__init__(self, params)
  21
+        global _caches, _expire_info, _locks
  22
+        self._cache = _caches.setdefault(name, {})
  23
+        self._expire_info = _expire_info.setdefault(name, {})
  24
+        self._lock = _locks.setdefault(name, RWLock())
18 25
 
19 26
     def add(self, key, value, timeout=None, version=None):
20 27
         key = self.make_key(key, version=version)
@@ -133,3 +140,7 @@ def delete(self, key, version=None):
133 140
     def clear(self):
134 141
         self._cache.clear()
135 142
         self._expire_info.clear()
  143
+
  144
+# For backwards compatibility
  145
+class CacheClass(LocMemCache):
  146
+    pass
117  django/core/cache/backends/memcached.py
... ...
@@ -1,26 +1,34 @@
1 1
 "Memcached cache backend"
2 2
 
3 3
 import time
  4
+from threading import local
4 5
 
5 6
 from django.core.cache.backends.base import BaseCache, InvalidCacheBackendError
6  
-
7  
-try:
8  
-    import cmemcache as memcache
9  
-    import warnings
10  
-    warnings.warn(
11  
-        "Support for the 'cmemcache' library has been deprecated. Please use python-memcached instead.",
12  
-        DeprecationWarning
13  
-    )
14  
-except ImportError:
15  
-    try:
16  
-        import memcache
17  
-    except:
18  
-        raise InvalidCacheBackendError("Memcached cache backend requires either the 'memcache' or 'cmemcache' library")
19  
-
20  
-class CacheClass(BaseCache):
21  
-    def __init__(self, server, params, key_prefix='', version=1, key_func=None):
22  
-        BaseCache.__init__(self, params, key_prefix, version, key_func)
23  
-        self._cache = memcache.Client(server.split(';'))
  7
+from django.utils import importlib
  8
+
  9
+class BaseMemcachedCache(BaseCache):
  10
+    def __init__(self, server, params, library, value_not_found_exception):
  11
+        super(BaseMemcachedCache, self).__init__(params)
  12
+        if isinstance(server, basestring):
  13
+            self._servers = server.split(';')
  14
+        else:
  15
+            self._servers = server
  16
+
  17
+        # The exception type to catch from the underlying library for a key
  18
+        # that was not found. This is a ValueError for python-memcache,
  19
+        # pylibmc.NotFound for pylibmc, and cmemcache will return None without
  20
+        # raising an exception.
  21
+        self.LibraryValueNotFoundException = value_not_found_exception
  22
+
  23
+        self._lib = library
  24
+        self._options = params.get('OPTIONS', None)
  25
+
  26
+    @property
  27
+    def _cache(self):
  28
+        """
  29
+        Implements transparent thread-safe access to a memcached client.
  30
+        """
  31
+        return self._lib.Client(self._servers)
24 32
 
25 33
     def _get_memcache_timeout(self, timeout):
26 34
         """
@@ -79,13 +87,13 @@ def incr(self, key, delta=1, version=None):
79 87
             val = self._cache.incr(key, delta)
80 88
 
81 89
         # python-memcache responds to incr on non-existent keys by
82  
-        # raising a ValueError. Cmemcache returns None. In both
83  
-        # cases, we should raise a ValueError though.
84  
-        except ValueError:
  90
+        # raising a ValueError, pylibmc by raising a pylibmc.NotFound
  91
+        # and Cmemcache returns None. In all cases,
  92
+        # we should raise a ValueError though.
  93
+        except self.LibraryValueNotFoundException:
85 94
             val = None
86 95
         if val is None:
87 96
             raise ValueError("Key '%s' not found" % key)
88  
-
89 97
         return val
90 98
 
91 99
     def decr(self, key, delta=1, version=None):
@@ -93,10 +101,11 @@ def decr(self, key, delta=1, version=None):
93 101
         try:
94 102
             val = self._cache.decr(key, delta)
95 103
 
96  
-        # python-memcache responds to decr on non-existent keys by
97  
-        # raising a ValueError. Cmemcache returns None. In both
98  
-        # cases, we should raise a ValueError though.
99  
-        except ValueError:
  104
+        # python-memcache responds to incr on non-existent keys by
  105
+        # raising a ValueError, pylibmc by raising a pylibmc.NotFound
  106
+        # and Cmemcache returns None. In all cases,
  107
+        # we should raise a ValueError though.
  108
+        except self.LibraryValueNotFoundException:
100 109
             val = None
101 110
         if val is None:
102 111
             raise ValueError("Key '%s' not found" % key)
@@ -117,3 +126,59 @@ def delete_many(self, keys, version=None):
117 126
 
118 127
     def clear(self):
119 128
         self._cache.flush_all()
  129
+
  130
+# For backwards compatibility -- the default cache class tries a
  131
+# cascading lookup of cmemcache, then memcache.
  132
+class CacheClass(BaseMemcachedCache):
  133
+    def __init__(self, server, params):
  134
+        try:
  135
+            import cmemcache as memcache
  136
+            import warnings
  137
+            warnings.warn(
  138
+                "Support for the 'cmemcache' library has been deprecated. Please use python-memcached or pyblimc instead.",
  139
+                DeprecationWarning
  140
+            )
  141
+        except ImportError:
  142
+            try:
  143
+                import memcache
  144
+            except:
  145
+                raise InvalidCacheBackendError(
  146
+                    "Memcached cache backend requires either the 'memcache' or 'cmemcache' library"
  147
+                    )
  148
+        super(CacheClass, self).__init__(server, params,
  149
+                                         library=memcache,
  150
+                                         value_not_found_exception=ValueError)
  151
+
  152
+class MemcachedCache(BaseMemcachedCache):
  153
+    "An implementation of a cache binding using python-memcached"
  154
+    def __init__(self, server, params):
  155
+        import memcache
  156
+        super(MemcachedCache, self).__init__(server, params,
  157
+                                             library=memcache,
  158
+                                             value_not_found_exception=ValueError)
  159
+
  160
+class PyLibMCCache(BaseMemcachedCache):
  161
+    "An implementation of a cache binding using pylibmc"
  162
+    def __init__(self, server, params):
  163
+        import pylibmc
  164
+        self._local = local()
  165
+        super(PyLibMCCache, self).__init__(server, params,
  166
+                                           library=pylibmc,
  167
+                                           value_not_found_exception=pylibmc.NotFound)
  168
+
  169
+    @property
  170
+    def _cache(self):
  171
+        # PylibMC uses cache options as the 'behaviors' attribute.
  172
+        # It also needs to use threadlocals, because some versions of
  173
+        # PylibMC don't play well with the GIL.
  174
+        client = getattr(self._local, 'client', None)
  175
+        if client:
  176
+            return client
  177
+
  178
+        client = self._lib.Client(self._servers)
  179
+        if self._options:
  180
+            client.behaviors = self._options
  181
+
  182
+        self._local.client = client
  183
+
  184
+        return client
14  django/db/backends/creation.py
@@ -359,12 +359,14 @@ def create_test_db(self, verbosity=1, autoclobber=False):
359 359
         # (unless you really ask to be flooded)
360 360
         call_command('syncdb', verbosity=max(verbosity - 1, 0), interactive=False, database=self.connection.alias)
361 361
 
362  
-        if settings.CACHE_BACKEND.startswith('db://'):
363  
-            from django.core.cache import parse_backend_uri, cache
364  
-            from django.db import router
365  
-            if router.allow_syncdb(self.connection.alias, cache.cache_model_class):
366  
-                _, cache_name, _ = parse_backend_uri(settings.CACHE_BACKEND)
367  
-                call_command('createcachetable', cache_name, database=self.connection.alias)
  362
+        from django.core.cache import get_cache
  363
+        from django.core.cache.backends.db import BaseDatabaseCache
  364
+        for cache_alias in settings.CACHES:
  365
+            cache = get_cache(cache_alias)
  366
+            if isinstance(cache, BaseDatabaseCache):
  367
+                from django.db import router
  368
+                if router.allow_syncdb(self.connection.alias, cache.cache_model_class):
  369
+                    call_command('createcachetable', cache._table, database=self.connection.alias)
368 370
 
369 371
         # Get a cursor (even though we don't need one yet). This has
370 372
         # the side effect of initializing the test database.
37  django/middleware/cache.py
@@ -49,7 +49,7 @@
49 49
 """
50 50
 
51 51
 from django.conf import settings
52  
-from django.core.cache import cache
  52
+from django.core.cache import get_cache, DEFAULT_CACHE_ALIAS
53 53
 from django.utils.cache import get_cache_key, learn_cache_key, patch_response_headers, get_max_age
54 54
 
55 55
 class UpdateCacheMiddleware(object):
@@ -65,6 +65,7 @@ def __init__(self):
65 65
         self.cache_timeout = settings.CACHE_MIDDLEWARE_SECONDS
66 66
         self.key_prefix = settings.CACHE_MIDDLEWARE_KEY_PREFIX
67 67
         self.cache_anonymous_only = getattr(settings, 'CACHE_MIDDLEWARE_ANONYMOUS_ONLY', False)
  68
+        self.cache = get_cache(settings.CACHE_MIDDLEWARE_ALIAS)
68 69
 
69 70
     def process_response(self, request, response):
70 71
         """Sets the cache, if needed."""
@@ -85,7 +86,7 @@ def process_response(self, request, response):
85 86
         patch_response_headers(response, timeout)
86 87
         if timeout:
87 88
             cache_key = learn_cache_key(request, response, timeout, self.key_prefix)
88  
-            cache.set(cache_key, response, timeout)
  89
+            self.cache.set(cache_key, response, timeout)
89 90
         return response
90 91
 
91 92
 class FetchFromCacheMiddleware(object):
@@ -100,6 +101,7 @@ def __init__(self):
100 101
         self.cache_timeout = settings.CACHE_MIDDLEWARE_SECONDS
101 102
         self.key_prefix = settings.CACHE_MIDDLEWARE_KEY_PREFIX
102 103
         self.cache_anonymous_only = getattr(settings, 'CACHE_MIDDLEWARE_ANONYMOUS_ONLY', False)
  104
+        self.cache = get_cache(settings.CACHE_MIDDLEWARE_ALIAS)
103 105
 
104 106
     def process_request(self, request):
105 107
         """
@@ -124,12 +126,12 @@ def process_request(self, request):
124 126
             request._cache_update_cache = True
125 127
             return None # No cache information available, need to rebuild.
126 128
 
127  
-        response = cache.get(cache_key, None)
  129
+        response = self.cache.get(cache_key, None)
128 130
 
129 131
         # if it wasn't found and we are looking for a HEAD, try looking just for that
130 132
         if response is None and request.method == 'HEAD':
131 133
             cache_key = get_cache_key(request, self.key_prefix, 'HEAD')
132  
-            response = cache.get(cache_key, None)
  134
+            response = self.cache.get(cache_key, None)
133 135
 
134 136
         if response is None:
135 137
             request._cache_update_cache = True
@@ -146,14 +148,33 @@ class CacheMiddleware(UpdateCacheMiddleware, FetchFromCacheMiddleware):
146 148
     Also used as the hook point for the cache decorator, which is generated
147 149
     using the decorator-from-middleware utility.
148 150
     """
149  
-    def __init__(self, cache_timeout=None, key_prefix=None, cache_anonymous_only=None):
  151
+    def __init__(self, cache_timeout=None, cache_anonymous_only=None, **kwargs):
150 152
         self.cache_timeout = cache_timeout
151 153
         if cache_timeout is None:
152 154
             self.cache_timeout = settings.CACHE_MIDDLEWARE_SECONDS
153  
-        self.key_prefix = key_prefix
154  
-        if key_prefix is None:
155  
-            self.key_prefix = settings.CACHE_MIDDLEWARE_KEY_PREFIX
  155
+
  156
+        # We need to differentiate between "provided, but using default value",
  157
+        # and "not provided". If the value is provided using a default, then
  158
+        # we fall back to system defaults. If it is not provided at all,
  159
+        # we need to use middleware defaults.
  160
+        try:
  161
+            cache_alias = kwargs.get('cache_alias')
  162
+            if cache_alias is None:
  163
+                cache_alias = DEFAULT_CACHE_ALIAS
  164
+        except KeyError:
  165
+            cache_alias = settings.CACHE_MIDDLEWARE_ALIAS
  166
+
  167
+        cache_kwargs = {}
  168
+        try:
  169
+            key_prefix = kwargs.get('key_prefix')
  170
+            if key_prefix is not None:
  171
+                cache_kwargs['KEY_PREFIX'] = key_prefix
  172
+        except KeyError:
  173
+            cache_kwargs['KEY_PREFIX'] = settings.CACHE_MIDDLEWARE_KEY_PREFIX
  174
+
156 175
         if cache_anonymous_only is None:
157 176
             self.cache_anonymous_only = getattr(settings, 'CACHE_MIDDLEWARE_ANONYMOUS_ONLY', False)
158 177
         else:
159 178
             self.cache_anonymous_only = cache_anonymous_only
  179
+
  180
+        self.cache = get_cache(cache_alias, **cache_kwargs)
13  django/views/decorators/cache.py
@@ -40,23 +40,24 @@ def cache_page(*args, **kwargs):
40 40
 
41 41
     # We also add some asserts to give better error messages in case people are
42 42
     # using other ways to call cache_page that no longer work.
  43
+    cache_alias = kwargs.pop('cache', None)
43 44
     key_prefix = kwargs.pop('key_prefix', None)
44  
-    assert not kwargs, "The only keyword argument accepted is key_prefix"
  45
+    assert not kwargs, "The only keyword arguments are cache and key_prefix"
45 46
     if len(args) > 1:
46 47
         assert len(args) == 2, "cache_page accepts at most 2 arguments"
47 48
         if callable(args[0]):
48  
-            return decorator_from_middleware_with_args(CacheMiddleware)(cache_timeout=args[1], key_prefix=key_prefix)(args[0])
  49
+            return decorator_from_middleware_with_args(CacheMiddleware)(cache_timeout=args[1], cache_alias=cache_alias, key_prefix=key_prefix)(args[0])
49 50
         elif callable(args[1]):
50  
-            return decorator_from_middleware_with_args(CacheMiddleware)(cache_timeout=args[0], key_prefix=key_prefix)(args[1])
  51
+            return decorator_from_middleware_with_args(CacheMiddleware)(cache_timeout=args[0], cache_alias=cache_alias, key_prefix=key_prefix)(args[1])
51 52
         else:
52 53
             assert False, "cache_page must be passed a view function if called with two arguments"
53 54
     elif len(args) == 1:
54 55
         if callable(args[0]):
55  
-            return decorator_from_middleware_with_args(CacheMiddleware)(key_prefix=key_prefix)(args[0])
  56
+            return decorator_from_middleware_with_args(CacheMiddleware)(cache_alias=cache_alias, key_prefix=key_prefix)(args[0])
56 57
         else:
57  
-            return decorator_from_middleware_with_args(CacheMiddleware)(cache_timeout=args[0], key_prefix=key_prefix)
  58
+            return decorator_from_middleware_with_args(CacheMiddleware)(cache_timeout=args[0], cache_alias=cache_alias, key_prefix=key_prefix)
58 59
     else:
59  
-        return decorator_from_middleware_with_args(CacheMiddleware)(key_prefix=key_prefix)
  60
+        return decorator_from_middleware_with_args(CacheMiddleware)(cache_alias=cache_alias, key_prefix=key_prefix)
60 61
 
61 62
 
62 63
 def cache_control(**kwargs):
2  docs/internals/contributing.txt
@@ -940,7 +940,7 @@ dependencies:
940 940
     *  gettext_ (:ref:`gettext_on_windows`)
941 941
 
942 942
 If you want to test the memcached cache backend, you will also need to define
943  
-a :setting:`CACHE_BACKEND` setting that points at your memcached instance.
  943
+a :setting:`CACHES` setting that points at your memcached instance.
944 944
 
945 945
 Each of these dependencies is optional. If you're missing any of them, the
946 946
 associated tests will be skipped.
150  docs/ref/settings.txt
@@ -127,21 +127,63 @@ Default: Not defined
127 127
 The site-specific user profile model used by this site. See
128 128
 :ref:`auth-profiles`.
129 129
 
130  
-.. setting:: CACHE_BACKEND
  130
+.. setting:: CACHES
131 131
 
132  
-CACHE_BACKEND
133  
--------------
  132
+CACHES
  133
+------
134 134
 
135  
-Default: ``'locmem://'``
  135
+.. versionadded:: 1.3
136 136
 
137  
-The cache backend to use. See :doc:`/topics/cache`.
  137
+Default::
138 138
 
139  
-.. setting:: CACHE_KEY_FUNCTION
  139
+    {
  140
+        'default': {
  141
+            'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
  142
+        }
  143
+    }
140 144
 
141  
-CACHE_KEY_FUNCTION
142  
-------------------
  145
+A dictionary containing the settings for all caches to be used with
  146
+Django. It is a nested dictionary whose contents maps cache aliases
  147
+to a dictionary containing the options for an individual cache.
143 148
 
144  
-Default: ``None``
  149
+The :setting:`CACHES` setting must configure a ``default`` cache;
  150
+any number of additional caches may also be specified. If you
  151
+are using a cache backend other than the local memory cache, or
  152
+you need to define multiple caches, other options will be required.
  153
+The following cache options are available.
  154
+
  155
+.. setting:: CACHES-BACKEND
  156
+
  157
+BACKEND
  158
+~~~~~~~
  159
+
  160
+Default: ``''`` (Empty string)
  161
+
  162
+The cache backend to use. The built-in cache backends are:
  163
+
  164
+    * ``'django.core.cache.backends.db.DatabaseCache'``
  165
+    * ``'django.core.cache.backends.dummy.DummyCache'``
  166
+    * ``'django.core.cache.backends.filebased.FileBasedCache'``
  167
+    * ``'django.core.cache.backends.locmem.LocMemCache'``
  168
+    * ``'django.core.cache.backends.memcached.MemcachedCache'``
  169
+    * ``'django.core.cache.backends.memcached.PyLibMCCache'``
  170
+
  171
+You can use a cache backend that doesn't ship with Django by setting
  172
+:setting:`BACKEND <CACHE-BACKEND>` to a fully-qualified path of a cache
  173
+backend class (i.e. ``mypackage.backends.whatever.WhateverCache``).
  174
+Writing a whole new cache backend from scratch is left as an exercise
  175
+to the reader; see the other backends for examples.
  176
+
  177
+.. note::
  178
+    Prior to Django 1.3, you could use a URI based version of the backend
  179
+    name to reference the built-in cache backends (e.g., you could use
  180
+    ``'db://tablename'`` to refer to the database backend). This format has
  181
+    been deprecated, and will be removed in Django 1.5.
  182
+
  183
+.. setting:: CACHES-KEY_FUNCTION
  184
+
  185
+KEY_FUNCTION
  186
+~~~~~~~~~~~~
145 187
 
146 188
 A string containing a dotted path to a function that defines how to
147 189
 compose a prefix, version and key into a final cache key. The default
@@ -155,10 +197,10 @@ argument signature.
155 197
 
156 198
 See the :ref:`cache documentation <cache_key_transformation>` for more information.
157 199
 
158  
-.. setting:: CACHE_KEY_PREFIX
  200
+.. setting:: CACHES-KEY_PREFIX
159 201
 
160  
-CACHE_KEY_PREFIX
161  
-----------------
  202
+KEY_PREFIX
  203
+~~~~~~~~~~
162 204
 
163 205
 Default: ``''`` (Empty string)
164 206
 
@@ -167,6 +209,67 @@ all cache keys used by the Django server.
167 209
 
168 210
 See the :ref:`cache documentation <cache_key_prefixing>` for more information.
169 211
 
  212
+.. setting:: CACHES-LOCATION
  213
+
  214
+LOCATION
  215
+~~~~~~~~
  216
+
  217
+Default: ``''`` (Empty string)
  218
+
  219
+The location of the cache to use. This might be the directory for a
  220
+file system cache, a host and port for a memcache server, or simply an
  221
+identifying name for a local memory cache. e.g.::
  222
+
  223
+    CACHES = {
  224
+        'default': {
  225
+            'BACKEND': 'django.core.cache.backends.filebased.FileBasedCache',
  226
+            'LOCATION': '/var/tmp/django_cache',
  227
+        }
  228
+    }
  229
+
  230
+.. setting:: CACHES-OPTIONS
  231
+
  232
+OPTIONS
  233
+~~~~~~~
  234
+
  235
+Default: None
  236
+
  237
+Extra parameters to pass to the cache backend. Available parameters
  238
+vary depending on your cache backend.
  239
+
  240
+Some information on available parameters can be found in the
  241
+:doc:`Cache Backends </topics/cache>` documentation. For more information,
  242
+consult your backend module's own documentation.
  243
+
  244
+.. setting:: CACHES-TIMEOUT
  245
+
  246
+TIMEOUT
  247
+~~~~~~~
  248
+
  249
+Default: 300
  250
+
  251
+The number of seconds before a cache entry is considered stale.
  252
+
  253
+.. setting:: CACHES-VERSION
  254
+
  255
+VERSION
  256
+~~~~~~~
  257
+
  258
+Default: ``1``
  259
+
  260
+The default version number for cache keys generated by the Django server.
  261
+
  262
+See the :ref:`cache documentation <cache_versioning>` for more information.
  263
+
  264
+.. setting:: CACHE_MIDDLEWARE_ALIAS
  265
+
  266
+CACHE_MIDDLEWARE_ALIAS
  267
+----------------------
  268
+
  269
+Default: ``default``
  270
+
  271
+The cache connection to use for the cache middleware.
  272
+
170 273
 .. setting:: CACHE_MIDDLEWARE_ANONYMOUS_ONLY
171 274
 
172 275
 CACHE_MIDDLEWARE_ANONYMOUS_ONLY
@@ -206,18 +309,6 @@ The default number of seconds to cache a page when the caching middleware or
206 309
 
207 310
 See :doc:`/topics/cache`.
208 311
 
209  
-.. setting:: CACHE_VERSION
210  
-
211  
-CACHE_VERSION
212  
--------------
213  
-
214  
-Default: ``1``
215  
-
216  
-The default version number for cache keys generated by the Django server.
217  
-
218  
-See the :ref:`cache documentation <cache_versioning>` for more information.
219  
-
220  
-
221 312
 .. setting:: CSRF_COOKIE_DOMAIN
222 313
 
223 314
 CSRF_COOKIE_DOMAIN
@@ -293,7 +384,7 @@ SQLite. This can be configured using the following::
293 384
 For other database backends, or more complex SQLite configurations, other options
294 385
 will be required. The following inner options are available.
295 386
 
296  
-.. setting:: ENGINE
  387
+.. setting:: DATABASE-ENGINE
297 388
 
298 389
 ENGINE
299 390
 ~~~~~~
@@ -1896,6 +1987,15 @@ See :tfilter:`allowed date format strings <date>`. See also ``DATE_FORMAT``,
1896 1987
 Deprecated settings
1897 1988
 ===================
1898 1989
 
  1990
+.. setting:: CACHE_BACKEND
  1991
+
  1992
+CACHE_BACKEND
  1993
+-------------
  1994
+
  1995
+.. deprecated:: 1.3
  1996
+   This setting has been replaced by :setting:`BACKEND <CACHES-BACKEND>` in
  1997
+   :setting:`CACHES`.
  1998
+
1899 1999
 .. setting:: DATABASE_ENGINE
1900 2000
 
1901 2001
 DATABASE_ENGINE
27  docs/releases/1.3.txt
@@ -154,6 +154,29 @@ it is needed, later in the response process.
154 154
 For more details, see the :ref:`documentation </ref/template-response>`
155 155
 on the :class:`~django.template.TemplateResponse` class.
156 156
 
  157
+Caching changes
  158
+~~~~~~~~~~~~~~~
  159
+
  160
+Django 1.3 sees the introduction of several improvements to the
  161
+Django's caching infrastructure.
  162
+
  163
+Firstly, Django now supports multiple named caches. In the same way
  164
+that Django 1.2 introduced support for multiple database connections,
  165
+Django 1.3 allows you to use the new :setting:`CACHES` setting to
  166
+define multiple named cache connections.
  167
+
  168
+Secondly, :ref:`Versioning <cache_versioning>`, :ref:`site-wide
  169
+prefixing <cache_key_prefixing>` and :ref:`transformation
  170
+<cache_key_transformation>` has been added to the cache API.
  171
+
  172
+Lastly, support for pylibmc_ has been added to the memcached cache
  173
+backend.
  174
+
  175
+For more details, see the :ref:`documentation on
  176
+caching in Django<topics/cache>`.
  177
+
  178
+.. _pylibmc: http://sendapatch.se/projects/pylibmc/
  179
+
157 180
 Everything else
158 181
 ~~~~~~~~~~~~~~~
159 182
 
@@ -176,10 +199,6 @@ requests. These include:
176 199
       :meth:`~django.test.client.Client.assertNumQueries` -- making it
177 200
       easier to test the database activity associated with a view.
178 201
 
179  
-    * :ref:`Versioning <cache_versioning>`, :ref:`site-wide prefixing
180  
-      <cache_key_prefixing>` and :ref:`transformation
181  
-      <cache_key_transformation>` has been added to the cache API.
182  
-
183 202
     * Support for lookups spanning relations in admin's ``list_filter``.
184 203
 
185 204
     * Support for _HTTPOnly cookies.
285  docs/topics/cache.txt
@@ -47,9 +47,16 @@ where your cached data should live -- whether in a database, on the filesystem
47 47
 or directly in memory. This is an important decision that affects your cache's
48 48
 performance; yes, some cache types are faster than others.
49 49
 
50  
-Your cache preference goes in the :setting:`CACHE_BACKEND` setting in your
  50
+Your cache preference goes in the :setting:`CACHES` setting in your
51 51
 settings file. Here's an explanation of all available values for
52  
-:setting:`CACHE_BACKEND`.
  52
+:setting:`CACHES`.
  53
+
  54
+.. versionchanged:: 1.3
  55
+    The settings used to configure caching changed in Django 1.3. In
  56
+    Django 1.2 and earlier, you used a single string-based
  57
+    :setting:`CACHE_BACKEND` setting to configure caches. This has
  58
+    been replaced with the new dictionary-based :setting:`CACHES`
  59
+    setting.
53 60
 
54 61
 Memcached
55 62
 ---------
@@ -66,9 +73,12 @@ fast interface for adding, retrieving and deleting arbitrary data in the cache.
66 73
 All data is stored directly in memory, so there's no overhead of database or
67 74
 filesystem usage.
68 75
 
69  
-After installing Memcached itself, you'll need to install
70  
-``python-memcached``, which provides Python bindings to Memcached.
71  
-This is available at ftp://ftp.tummy.com/pub/python-memcached/
  76
+After installing Memcached itself, you'll need to install a memcached
  77
+binding. There are several python memcached bindings available; the
  78
+two most common are `python-memcached`_ and `pylibmc`_.
  79
+
  80
+.. _`python-memcached`: ftp://ftp.tummy.com/pub/python-memcached/
  81
+.. _`pylibmc`: http://sendapatch.se/projects/pylibmc/
72 82
 
73 83
 .. versionchanged:: 1.2
74 84
     In Django 1.0 and 1.1, you could also use ``cmemcache`` as a binding.
@@ -76,31 +86,64 @@ This is available at ftp://ftp.tummy.com/pub/python-memcached/
76 86
     a lack of maintenance on the ``cmemcache`` library itself. Support for
77 87
     ``cmemcache`` will be removed completely in Django 1.4.
78 88
 
79  
-To use Memcached with Django, set :setting:`CACHE_BACKEND` to
80  
-``memcached://ip:port/``, where ``ip`` is the IP address of the Memcached
81  
-daemon and ``port`` is the port on which Memcached is running.
  89
+.. versionchanged:: 1.3
  90
+    Support for ``pylibmc`` was added.
  91
+
  92
+To use Memcached with Django:
  93
+
  94
+    * Set :setting:`BACKEND <CACHES-BACKEND>` to
  95
+      ``django.core.cache.backends.memcached.MemcachedCache`` or
  96
+      ``django.core.cache.backends.memcached.PyLibMCCache`` (depending
  97
+      on your chosen memcached binding)
  98
+
  99
+    * Set :setting:`LOCATION <CACHES-LOCATION>` to ``ip:port`` values,
  100
+      where ``ip`` is the IP address of the Memcached daemon and
  101
+      ``port`` is the port on which Memcached is running.
82 102
 
83  
-In this example, Memcached is running on localhost (127.0.0.1) port 11211::
  103
+In this example, Memcached is running on localhost (127.0.0.1) port 11211, using
  104
+the ``python-memcached`` binding::
84 105
 
85  
-    CACHE_BACKEND = 'memcached://127.0.0.1:11211/'
  106
+    CACHES = {
  107
+        'default': {
  108
+            'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
  109
+            'LOCATION': '127.0.0.1:11211',
  110
+        }
  111
+    }
86 112
 
87 113
 One excellent feature of Memcached is its ability to share cache over multiple
88 114
 servers. This means you can run Memcached daemons on multiple machines, and the
89 115
 program will treat the group of machines as a *single* cache, without the need
90 116
 to duplicate cache values on each machine. To take advantage of this feature,
91  
-include all server addresses in :setting:`CACHE_BACKEND`, separated by
92  
-semicolons.
  117
+include all server addresses in :setting:`BACKEND <CACHES-BACKEND>`, either
  118
+separated by semicolons or as a list.
93 119
 
94 120
 In this example, the cache is shared over Memcached instances running on IP
95 121
 address 172.19.26.240 and 172.19.26.242, both on port 11211::
96 122
 
97  
-    CACHE_BACKEND = 'memcached://172.19.26.240:11211;172.19.26.242:11211/'
  123
+    CACHES = {
  124
+        'default': {
  125
+            'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
  126
+            'LOCATION': [
  127
+                '172.19.26.240:11211',
  128
+                '172.19.26.242:11211',
  129
+            ]
  130
+        }
  131
+    }
98 132
 
99 133
 In the following example, the cache is shared over Memcached instances running
100 134
 on the IP addresses 172.19.26.240 (port 11211), 172.19.26.242 (port 11212), and
101 135
 172.19.26.244 (port 11213)::
102 136
 
103  
-    CACHE_BACKEND = 'memcached://172.19.26.240:11211;172.19.26.242:11212;172.19.26.244:11213/'
  137
+    CACHES = {
  138
+        'default': {
  139
+            'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
  140
+            'LOCATION': [
  141
+                '172.19.26.240:11211',
  142
+                '172.19.26.242:11211',
  143
+                '172.19.26.244:11213',
  144
+            ]
  145
+        }
  146
+    }
104 147
 
105 148
 A final point about Memcached is that memory-based caching has one
106 149
 disadvantage: Because the cached data is stored in memory, the data will be
@@ -125,12 +168,19 @@ not already being used in your database.) This command creates a single table
125 168
 in your database that is in the proper format that Django's database-cache
126 169
 system expects.
127 170
 
128  
-Once you've created that database table, set your :setting:`CACHE_BACKEND`
129  
-setting to ``"db://tablename"``, where ``tablename`` is the name of the
130  
-database table. In this example, the cache table's name is
131  
-``my_cache_table``::
  171
+Once you've created that database table, set your
  172
+:setting:`BACKEND <CACHES-BACKEND>` setting to
  173
+``"django.core.cache.backends.db.DatabaseCache"``, and
  174
+:setting:`LOCATION <CACHES-LOCATION>` to ``tablename`` -- the name of the
  175
+database table. In this example, the cache table's name is ``my_cache_table``::
  176
+
  177
+    CACHES = {
  178
+        'default': {
  179
+            'BACKEND': 'django.core.cache.backends.db.DatabaseCache',
  180
+            'LOCATION': 'my_cache_table',
  181
+        }
  182
+    }
132 183
 
133  
-    CACHE_BACKEND = 'db://my_cache_table'
134 184
 
135 185
 The database caching backend uses the same database as specified in your
136 186
 settings file. You can't use a different database backend for your cache table.
@@ -183,18 +233,28 @@ model.
183 233
 Filesystem caching
184 234
 ------------------
185 235
 
186  
-To store cached items on a filesystem, use the ``"file://"`` cache type for
187  
-:setting:`CACHE_BACKEND`. For example, to store cached data in
  236
+To store cached items on a filesystem, use
  237
+``"django.core.cache.backends.filebased.FileBasedCache"`` for