Index: django/conf/global_settings.py
===================================================================
--- django/conf/global_settings.py	(revision 13448)
+++ django/conf/global_settings.py	(working copy)
@@ -427,6 +427,7 @@
 # The cache backend to use.  See the docstring in django.core.cache for the
 # possible values.
 CACHE_BACKEND = 'locmem://'
+CACHE_KEY_PREFIX = ''
 CACHE_MIDDLEWARE_KEY_PREFIX = ''
 CACHE_MIDDLEWARE_SECONDS = 600
 
Index: django/core/cache/__init__.py
===================================================================
--- django/core/cache/__init__.py	(revision 13448)
+++ django/core/cache/__init__.py	(working copy)
@@ -56,18 +56,18 @@
 
     return scheme, host, params
 
-def get_cache(backend_uri):
+def get_cache(backend_uri, key_prefix=settings.CACHE_KEY_PREFIX):
     scheme, host, params = parse_backend_uri(backend_uri)
     if scheme in BACKENDS:
         name = 'django.core.cache.backends.%s' % BACKENDS[scheme]
     else:
         name = scheme
     module = importlib.import_module(name)
-    return getattr(module, 'CacheClass')(host, params)
+    return getattr(module, 'CacheClass')(host, params, key_prefix)
 
 cache = get_cache(settings.CACHE_BACKEND)
 
-# Some caches -- pythont-memcached in particular -- need to do a cleanup at the
+# Some caches -- python-memcached in particular -- need to do a cleanup at the
 # end of a request cycle. If the cache provides a close() method, wire it up
 # here.
 if hasattr(cache, 'close'):
Index: django/core/cache/backends/base.py
===================================================================
--- django/core/cache/backends/base.py	(revision 13448)
+++ django/core/cache/backends/base.py	(working copy)
@@ -1,19 +1,28 @@
 "Base Cache class."
 
 from django.core.exceptions import ImproperlyConfigured
+from django.utils.encoding import smart_str
 
 class InvalidCacheBackendError(ImproperlyConfigured):
     pass
 
 class BaseCache(object):
-    def __init__(self, params):
+    def __init__(self, params, key_prefix=''):
         timeout = params.get('timeout', 300)
         try:
             timeout = int(timeout)
         except (ValueError, TypeError):
             timeout = 300
         self.default_timeout = timeout
+        self.key_prefix = smart_str(key_prefix)
 
+    def make_key(self, key):
+        """Constructs the key used by all other methods. By default it prepends
+        the `key_prefix'. In cache backend subclasses this can be overriden to
+        provide custom key making behavior.
+        """
+        return self.key_prefix + smart_str(key)
+
     def add(self, key, value, timeout=None):
         """
         Set a value in the cache if the key does not already exist. If
Index: django/core/cache/backends/locmem.py
===================================================================
--- django/core/cache/backends/locmem.py	(revision 13448)
+++ django/core/cache/backends/locmem.py	(working copy)
@@ -10,8 +10,8 @@
 from django.utils.synch import RWLock
 
 class CacheClass(BaseCache):
-    def __init__(self, _, params):
-        BaseCache.__init__(self, params)
+    def __init__(self, _, params, key_prefix):
+        BaseCache.__init__(self, params, key_prefix='')
         self._cache = {}
         self._expire_info = {}
 
@@ -30,6 +30,7 @@
         self._lock = RWLock()
 
     def add(self, key, value, timeout=None):
+        key = self.make_key(key)
         self._lock.writer_enters()
         try:
             exp = self._expire_info.get(key)
@@ -44,6 +45,7 @@
             self._lock.writer_leaves()
 
     def get(self, key, default=None):
+        key = self.make_key(key)
         self._lock.reader_enters()
         try:
             exp = self._expire_info.get(key)
@@ -76,6 +78,7 @@
         self._expire_info[key] = time.time() + timeout
 
     def set(self, key, value, timeout=None):
+        key = self.make_key(key)
         self._lock.writer_enters()
         # Python 2.4 doesn't allow combined try-except-finally blocks.
         try:
@@ -87,6 +90,7 @@
             self._lock.writer_leaves()
 
     def has_key(self, key):
+        key = self.make_key(key)
         self._lock.reader_enters()
         try:
             exp = self._expire_info.get(key)
@@ -127,6 +131,7 @@
             pass
 
     def delete(self, key):
+        key = self.make_key(key)
         self._lock.writer_enters()
         try:
             self._delete(key)
Index: django/core/cache/backends/filebased.py
===================================================================
--- django/core/cache/backends/filebased.py	(revision 13448)
+++ django/core/cache/backends/filebased.py	(working copy)
@@ -12,8 +12,8 @@
 from django.utils.hashcompat import md5_constructor
 
 class CacheClass(BaseCache):
-    def __init__(self, dir, params):
-        BaseCache.__init__(self, params)
+    def __init__(self, dir, params, key_prefix=''):
+        BaseCache.__init__(self, params, key_prefix)
 
         max_entries = params.get('max_entries', 300)
         try:
@@ -32,6 +32,7 @@
             self._createdir()
 
     def add(self, key, value, timeout=None):
+        key = self.make_key(key)
         if self.has_key(key):
             return False
 
@@ -39,6 +40,7 @@
         return True
 
     def get(self, key, default=None):
+        key = self.make_key(key)
         fname = self._key_to_file(key)
         try:
             f = open(fname, 'rb')
@@ -56,6 +58,7 @@
         return default
 
     def set(self, key, value, timeout=None):
+        key = self.make_key(key)
         fname = self._key_to_file(key)
         dirname = os.path.dirname(fname)
 
@@ -79,6 +82,7 @@
             pass
 
     def delete(self, key):
+        key = self.make_key(key)
         try:
             self._delete(self._key_to_file(key))
         except (IOError, OSError):
@@ -95,6 +99,7 @@
             pass
 
     def has_key(self, key):
+        key = self.make_key(key)
         fname = self._key_to_file(key)
         try:
             f = open(fname, 'rb')
@@ -148,7 +153,8 @@
         Thus, a cache key of "foo" gets turnned into a file named
         ``{cache-dir}ac/bd/18db4cc2f85cedef654fccc4a4d8``.
         """
-        path = md5_constructor(key.encode('utf-8')).hexdigest()
+        key = self.make_key(key)
+        path = md5_constructor(key).hexdigest()
         path = os.path.join(path[:2], path[2:4], path[4:])
         return os.path.join(self._dir, path)
 
Index: django/core/cache/backends/db.py
===================================================================
--- django/core/cache/backends/db.py	(revision 13448)
+++ django/core/cache/backends/db.py	(working copy)
@@ -10,8 +10,8 @@
     import pickle
 
 class CacheClass(BaseCache):
-    def __init__(self, table, params):
-        BaseCache.__init__(self, params)
+    def __init__(self, table, params, key_prefix=''):
+        BaseCache.__init__(self, params, key_prefix)
         self._table = connection.ops.quote_name(table)
         max_entries = params.get('max_entries', 300)
         try:
@@ -25,6 +25,7 @@
             self._cull_frequency = 3
 
     def get(self, key, default=None):
+        key = self.make_key(key)
         cursor = connection.cursor()
         cursor.execute("SELECT cache_key, value, expires FROM %s WHERE cache_key = %%s" % self._table, [key])
         row = cursor.fetchone()
@@ -45,6 +46,7 @@
         return self._base_set('add', key, value, timeout)
 
     def _base_set(self, mode, key, value, timeout=None):
+        key = self.make_key(key)
         if timeout is None:
             timeout = self.default_timeout
         cursor = connection.cursor()
@@ -74,11 +76,13 @@
             return True
 
     def delete(self, key):
+        key = self.make_key(key)
         cursor = connection.cursor()
         cursor.execute("DELETE FROM %s WHERE cache_key = %%s" % self._table, [key])
         transaction.commit_unless_managed()
 
     def has_key(self, key):
+        key = self.make_key(key)
         now = datetime.now().replace(microsecond=0)
         cursor = connection.cursor()
         cursor.execute("SELECT cache_key FROM %s WHERE cache_key = %%s and expires > %%s" % self._table,
Index: django/core/cache/backends/memcached.py
===================================================================
--- django/core/cache/backends/memcached.py	(revision 13448)
+++ django/core/cache/backends/memcached.py	(working copy)
@@ -3,7 +3,6 @@
 import time
 
 from django.core.cache.backends.base import BaseCache, InvalidCacheBackendError
-from django.utils.encoding import smart_unicode, smart_str
 
 try:
     import cmemcache as memcache
@@ -19,8 +18,8 @@
         raise InvalidCacheBackendError("Memcached cache backend requires either the 'memcache' or 'cmemcache' library")
 
 class CacheClass(BaseCache):
-    def __init__(self, server, params):
-        BaseCache.__init__(self, params)
+    def __init__(self, server, params, key_prefix=''):
+        BaseCache.__init__(self, params, key_prefix)
         self._cache = memcache.Client(server.split(';'))
 
     def _get_memcache_timeout(self, timeout):
@@ -40,29 +39,35 @@
         return timeout
 
     def add(self, key, value, timeout=0):
+        key = self.make_key(key)
         if isinstance(value, unicode):
             value = value.encode('utf-8')
-        return self._cache.add(smart_str(key), value, self._get_memcache_timeout(timeout))
+        return self._cache.add(key, value, self._get_memcache_timeout(timeout))
 
     def get(self, key, default=None):
-        val = self._cache.get(smart_str(key))
+        key = self.make_key(key)
+        val = self._cache.get(key)
         if val is None:
             return default
         return val
 
     def set(self, key, value, timeout=0):
-        self._cache.set(smart_str(key), value, self._get_memcache_timeout(timeout))
+        key = self.make_key(key)
+        self._cache.set(key, value, self._get_memcache_timeout(timeout))
 
     def delete(self, key):
-        self._cache.delete(smart_str(key))
+        key = self.make_key(key)
+        self._cache.delete(key)
 
     def get_many(self, keys):
-        return self._cache.get_multi(map(smart_str,keys))
+        keys = map(self.make_key, keys)
+        return self._cache.get_multi(keys)
 
     def close(self, **kwargs):
         self._cache.disconnect_all()
 
     def incr(self, key, delta=1):
+        key = self.make_key(key)
         try:
             val = self._cache.incr(key, delta)
 
@@ -77,6 +82,7 @@
         return val
 
     def decr(self, key, delta=1):
+        key = self.make_key(key)
         try:
             val = self._cache.decr(key, delta)
 
@@ -92,13 +98,15 @@
     def set_many(self, data, timeout=0):
         safe_data = {}
         for key, value in data.items():
+            key = self.make_key(key)
             if isinstance(value, unicode):
                 value = value.encode('utf-8')
-            safe_data[smart_str(key)] = value
+            safe_data[key] = value
         self._cache.set_multi(safe_data, self._get_memcache_timeout(timeout))
 
     def delete_many(self, keys):
-        self._cache.delete_multi(map(smart_str, keys))
+        keys = map(self.make_key, keys)
+        self._cache.delete_multi(keys)
 
     def clear(self):
         self._cache.flush_all()
Index: tests/regressiontests/cache/tests.py
===================================================================
--- tests/regressiontests/cache/tests.py	(revision 13448)
+++ tests/regressiontests/cache/tests.py	(working copy)
@@ -143,7 +143,6 @@
         "clear does nothing for the dummy cache backend"
         self.cache.clear()
 
-
 class BaseCacheTests(object):
     # A common set of tests to apply to all cache backends
     def tearDown(self):
@@ -161,6 +160,18 @@
         self.assertEqual(result, False)
         self.assertEqual(self.cache.get("addkey1"), "value")
 
+    def test_prefix(self):
+        # Test for same cache key conflicts between shared backend
+        self.cache.set('somekey', 'value')
+
+        # should not be set in the prefixed cache
+        self.assertFalse(self.pfx_cache.has_key('somekey'))
+
+        self.pfx_cache.set('somekey', 'value2')
+
+        self.assertEqual(self.cache.get('somekey'), 'value')
+        self.assertEqual(self.pfx_cache.get('somekey'), 'value2')
+
     def test_non_existent(self):
         # Non-existent cache keys return as None/default
         # get with non-existent keys
@@ -358,6 +369,7 @@
         self._table_name = 'test cache table'
         management.call_command('createcachetable', self._table_name, verbosity=0, interactive=False)
         self.cache = get_cache('db://%s' % self._table_name)
+        self.pfx_cache = get_cache('db://%s' % self._table_name, 'cacheprefix')
 
     def tearDown(self):
         from django.db import connection
@@ -367,6 +379,7 @@
 class LocMemCacheTests(unittest.TestCase, BaseCacheTests):
     def setUp(self):
         self.cache = get_cache('locmem://')
+        self.pfx_cache = get_cache('locmem://', 'cacheprefix')
 
 # memcached backend isn't guaranteed to be available.
 # To check the memcached backend, the test settings file will
@@ -376,6 +389,7 @@
     class MemcachedCacheTests(unittest.TestCase, BaseCacheTests):
         def setUp(self):
             self.cache = get_cache(settings.CACHE_BACKEND)
+            self.pfx_cache = get_cache(settings.CACHE_BACKEND, 'cacheprefix')
 
 class FileBasedCacheTests(unittest.TestCase, BaseCacheTests):
     """
@@ -384,6 +398,7 @@
     def setUp(self):
         self.dirname = tempfile.mkdtemp()
         self.cache = get_cache('file://%s' % self.dirname)
+        self.pfx_cache = get_cache('file://%s' % self.dirname, 'cacheprefix')
 
     def test_hashing(self):
         """Test that keys are hashed into subdirectories correctly"""
@@ -411,16 +426,16 @@
 
     def setUp(self):
         self.path = '/cache/test/'
-        self.old_settings_key_prefix = settings.CACHE_MIDDLEWARE_KEY_PREFIX
-        self.old_middleware_seconds = settings.CACHE_MIDDLEWARE_SECONDS
+        self.old_cache_middleware_key_prefix = settings.CACHE_MIDDLEWARE_KEY_PREFIX
+        self.old_cache_middleware_seconds = settings.CACHE_MIDDLEWARE_SECONDS
         self.orig_use_i18n = settings.USE_I18N
         settings.CACHE_MIDDLEWARE_KEY_PREFIX = 'settingsprefix'
         settings.CACHE_MIDDLEWARE_SECONDS = 1
         settings.USE_I18N = False
 
     def tearDown(self):
-        settings.CACHE_MIDDLEWARE_KEY_PREFIX = self.old_settings_key_prefix
-        settings.CACHE_MIDDLEWARE_SECONDS = self.old_middleware_seconds
+        settings.CACHE_MIDDLEWARE_KEY_PREFIX = self.old_cache_middleware_key_prefix
+        settings.CACHE_MIDDLEWARE_SECONDS = self.old_cache_middleware_seconds
         settings.USE_I18N = self.orig_use_i18n
 
     def _get_request(self, path):
@@ -473,6 +488,16 @@
         learn_cache_key(request, response)
         self.assertEqual(get_cache_key(request), 'views.decorators.cache.cache_page.settingsprefix.a8c87a3d8c44853d7f79474f7ffe4ad5.d41d8cd98f00b204e9800998ecf8427e')
 
+class PrefixedCacheUtils(CacheUtils):
+    def setUp(self):
+        super(PrefixedCacheUtils, self).setUp()
+        self.old_cache_key_prefix = settings.CACHE_KEY_PREFIX
+        settings.CACHE_KEY_PREFIX = 'cacheprefix'
+
+    def tearDown(self):
+        super(PrefixedCacheUtils, self).tearDown()
+        settings.CACHE_KEY_PREFIX = self.old_cache_key_prefix
+
 class CacheI18nTest(unittest.TestCase):
 
     def setUp(self):
@@ -568,5 +593,15 @@
         get_cache_data = FetchFromCacheMiddleware().process_request(request)
         self.assertEqual(get_cache_data.content, es_message)
 
+class PrefixedCacheI18nTest(CacheI18nTest):
+    def setUp(self):
+        super(PrefixedCacheI18nTest, self).setUp()
+        self.old_cache_key_prefix = settings.CACHE_KEY_PREFIX
+        settings.CACHE_KEY_PREFIX = 'cacheprefix'
+
+    def tearDown(self):
+        super(PrefixedCacheI18nTest, self).tearDown()
+        settings.CACHE_KEY_PREFIX = self.old_cache_key_prefix
+
 if __name__ == '__main__':
     unittest.main()
Index: docs/topics/cache.txt
===================================================================
--- docs/topics/cache.txt	(revision 13448)
+++ docs/topics/cache.txt	(working copy)
@@ -600,6 +600,34 @@
     However, if the backend doesn't natively provide an increment/decrement
     operation, it will be implemented using a two-step retrieve/update.
 
+CACHE_KEY_PREFIX
+----------------
+
+It is a common occurence to have a shared cache instance running on your development
+or production server for use across multiple projects, i.e. all sites are pointing
+to the memcached instance running on port 11211. As a result, cache key conflicts
+may arise which could result in data from one site being used by another.
+
+To alleviate this, CACHE_KEY_PREFIX can be set. This will be prepended to all keys
+that are used with the cache backend, transparently::
+
+    # in settings, CACHE_KEY_PREFIX = 'myproject_'
+
+    >>> cache.set('my_key', 'hello world!') # set with key 'myproject_my_key'
+    >>> cache.get('my_key') # retrieved with key 'myproject_my_key'
+    'hello world!'
+
+Of course, for sites that _do_ share content, simply set the CACHE_KEY_PREFIX for
+both sites to the same value. The default value for CACHE_KEY_PREFIX is the empty
+string ``''``.
+
+.. note::
+   
+   This does *not* conflict with the CACHE_MIDDLEWARE_KEY_PREFIX and can be used
+   in conjunction with it. CACHE_KEY_PREFIX acts as a global prefix for a
+   particular cache instance, therefore it will be prepended to the
+   CACHE_MIDDLEWARE_KEY_PREFIX transparently.
+
 Upstream caches
 ===============
 
