Fixed #21012 -- Thread-local caches, like databases.
This commit is contained in:
parent
3ca0815c0b
commit
ee7eb0f73e
|
@ -1,6 +1,6 @@
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.contrib.sessions.backends.base import SessionBase, CreateError
|
from django.contrib.sessions.backends.base import SessionBase, CreateError
|
||||||
from django.core.cache import get_cache
|
from django.core.cache import caches
|
||||||
from django.utils.six.moves import xrange
|
from django.utils.six.moves import xrange
|
||||||
|
|
||||||
KEY_PREFIX = "django.contrib.sessions.cache"
|
KEY_PREFIX = "django.contrib.sessions.cache"
|
||||||
|
@ -11,7 +11,7 @@ class SessionStore(SessionBase):
|
||||||
A cache-based session store.
|
A cache-based session store.
|
||||||
"""
|
"""
|
||||||
def __init__(self, session_key=None):
|
def __init__(self, session_key=None):
|
||||||
self._cache = get_cache(settings.SESSION_CACHE_ALIAS)
|
self._cache = caches[settings.SESSION_CACHE_ALIAS]
|
||||||
super(SessionStore, self).__init__(session_key)
|
super(SessionStore, self).__init__(session_key)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
|
|
@ -6,7 +6,7 @@ import logging
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.contrib.sessions.backends.db import SessionStore as DBStore
|
from django.contrib.sessions.backends.db import SessionStore as DBStore
|
||||||
from django.core.cache import get_cache
|
from django.core.cache import caches
|
||||||
from django.core.exceptions import SuspiciousOperation
|
from django.core.exceptions import SuspiciousOperation
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from django.utils.encoding import force_text
|
from django.utils.encoding import force_text
|
||||||
|
@ -20,7 +20,7 @@ class SessionStore(DBStore):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, session_key=None):
|
def __init__(self, session_key=None):
|
||||||
self._cache = get_cache(settings.SESSION_CACHE_ALIAS)
|
self._cache = caches[settings.SESSION_CACHE_ALIAS]
|
||||||
super(SessionStore, self).__init__(session_key)
|
super(SessionStore, self).__init__(session_key)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
|
|
@ -15,7 +15,7 @@ from django.contrib.sessions.backends.file import SessionStore as FileSession
|
||||||
from django.contrib.sessions.backends.signed_cookies import SessionStore as CookieSession
|
from django.contrib.sessions.backends.signed_cookies import SessionStore as CookieSession
|
||||||
from django.contrib.sessions.models import Session
|
from django.contrib.sessions.models import Session
|
||||||
from django.contrib.sessions.middleware import SessionMiddleware
|
from django.contrib.sessions.middleware import SessionMiddleware
|
||||||
from django.core.cache import get_cache
|
from django.core.cache import caches
|
||||||
from django.core.cache.backends.base import InvalidCacheBackendError
|
from django.core.cache.backends.base import InvalidCacheBackendError
|
||||||
from django.core import management
|
from django.core import management
|
||||||
from django.core.exceptions import ImproperlyConfigured
|
from django.core.exceptions import ImproperlyConfigured
|
||||||
|
@ -140,7 +140,7 @@ class SessionTestsMixin(object):
|
||||||
self.assertTrue(self.session.modified)
|
self.assertTrue(self.session.modified)
|
||||||
|
|
||||||
def test_save(self):
|
def test_save(self):
|
||||||
if (hasattr(self.session, '_cache') and'DummyCache' in
|
if (hasattr(self.session, '_cache') and 'DummyCache' in
|
||||||
settings.CACHES[settings.SESSION_CACHE_ALIAS]['BACKEND']):
|
settings.CACHES[settings.SESSION_CACHE_ALIAS]['BACKEND']):
|
||||||
raise unittest.SkipTest("Session saving tests require a real cache backend")
|
raise unittest.SkipTest("Session saving tests require a real cache backend")
|
||||||
self.session.save()
|
self.session.save()
|
||||||
|
@ -481,7 +481,7 @@ class CacheSessionTests(SessionTestsMixin, unittest.TestCase):
|
||||||
|
|
||||||
def test_default_cache(self):
|
def test_default_cache(self):
|
||||||
self.session.save()
|
self.session.save()
|
||||||
self.assertNotEqual(get_cache('default').get(self.session.cache_key), None)
|
self.assertNotEqual(caches['default'].get(self.session.cache_key), None)
|
||||||
|
|
||||||
@override_settings(CACHES={
|
@override_settings(CACHES={
|
||||||
'default': {
|
'default': {
|
||||||
|
@ -489,6 +489,7 @@ class CacheSessionTests(SessionTestsMixin, unittest.TestCase):
|
||||||
},
|
},
|
||||||
'sessions': {
|
'sessions': {
|
||||||
'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
|
'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
|
||||||
|
'LOCATION': 'session',
|
||||||
},
|
},
|
||||||
}, SESSION_CACHE_ALIAS='sessions')
|
}, SESSION_CACHE_ALIAS='sessions')
|
||||||
def test_non_default_cache(self):
|
def test_non_default_cache(self):
|
||||||
|
@ -496,8 +497,8 @@ class CacheSessionTests(SessionTestsMixin, unittest.TestCase):
|
||||||
self.session = self.backend()
|
self.session = self.backend()
|
||||||
|
|
||||||
self.session.save()
|
self.session.save()
|
||||||
self.assertEqual(get_cache('default').get(self.session.cache_key), None)
|
self.assertEqual(caches['default'].get(self.session.cache_key), None)
|
||||||
self.assertNotEqual(get_cache('sessions').get(self.session.cache_key), None)
|
self.assertNotEqual(caches['sessions'].get(self.session.cache_key), None)
|
||||||
|
|
||||||
|
|
||||||
class SessionMiddlewareTests(unittest.TestCase):
|
class SessionMiddlewareTests(unittest.TestCase):
|
||||||
|
|
|
@ -7,7 +7,7 @@ import posixpath
|
||||||
import re
|
import re
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.core.cache import (get_cache, InvalidCacheBackendError,
|
from django.core.cache import (caches, InvalidCacheBackendError,
|
||||||
cache as default_cache)
|
cache as default_cache)
|
||||||
from django.core.exceptions import ImproperlyConfigured
|
from django.core.exceptions import ImproperlyConfigured
|
||||||
from django.core.files.base import ContentFile
|
from django.core.files.base import ContentFile
|
||||||
|
@ -56,7 +56,7 @@ class CachedFilesMixin(object):
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super(CachedFilesMixin, self).__init__(*args, **kwargs)
|
super(CachedFilesMixin, self).__init__(*args, **kwargs)
|
||||||
try:
|
try:
|
||||||
self.cache = get_cache('staticfiles')
|
self.cache = caches['staticfiles']
|
||||||
except InvalidCacheBackendError:
|
except InvalidCacheBackendError:
|
||||||
# Use the default backend
|
# Use the default backend
|
||||||
self.cache = default_cache
|
self.cache = default_cache
|
||||||
|
|
|
@ -14,6 +14,9 @@ class.
|
||||||
|
|
||||||
See docs/topics/cache.txt for information on the public API.
|
See docs/topics/cache.txt for information on the public API.
|
||||||
"""
|
"""
|
||||||
|
from threading import local
|
||||||
|
import warnings
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.core import signals
|
from django.core import signals
|
||||||
from django.core.cache.backends.base import (
|
from django.core.cache.backends.base import (
|
||||||
|
@ -23,8 +26,8 @@ from django.utils.module_loading import import_by_path
|
||||||
|
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
'get_cache', 'cache', 'DEFAULT_CACHE_ALIAS', 'InvalidCacheBackendError',
|
'create_cache', 'get_cache', 'cache', 'DEFAULT_CACHE_ALIAS',
|
||||||
'CacheKeyWarning', 'BaseCache',
|
'InvalidCacheBackendError', 'CacheKeyWarning', 'BaseCache',
|
||||||
]
|
]
|
||||||
|
|
||||||
DEFAULT_CACHE_ALIAS = 'default'
|
DEFAULT_CACHE_ALIAS = 'default'
|
||||||
|
@ -35,43 +38,61 @@ if DEFAULT_CACHE_ALIAS not in settings.CACHES:
|
||||||
|
|
||||||
def get_cache(backend, **kwargs):
|
def get_cache(backend, **kwargs):
|
||||||
"""
|
"""
|
||||||
Function to load a cache backend dynamically. This is flexible by design
|
Function to retrieve a configure cache, or create a new one.
|
||||||
to allow different use cases:
|
|
||||||
|
|
||||||
To load a backend that is pre-defined in the settings::
|
This wrapper is for backward compatibility.
|
||||||
|
|
||||||
cache = get_cache('default')
|
Use either create_cache or caches directly.
|
||||||
|
|
||||||
To load a backend with its dotted import path,
|
|
||||||
including arbitrary options::
|
|
||||||
|
|
||||||
cache = get_cache('django.core.cache.backends.memcached.MemcachedCache', **{
|
|
||||||
'LOCATION': '127.0.0.1:11211', 'TIMEOUT': 30,
|
|
||||||
})
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
warnings.warn("'get_cache' is deprecated. Use either caches or create_cache.",
|
||||||
|
PendingDeprecationWarning, stacklevel=2)
|
||||||
|
|
||||||
|
# If it's just an alias with no options, use the new API
|
||||||
|
if backend in settings.CACHES and not kwargs:
|
||||||
|
return caches[backend]
|
||||||
|
|
||||||
|
return create_cache(backend, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
def create_cache(backend, **params):
|
||||||
|
"""
|
||||||
|
Function to create a cache backend dynamically. This is flexible by design
|
||||||
|
to allow different use cases:
|
||||||
|
|
||||||
|
To load a backend with its dotted import path, including options::
|
||||||
|
|
||||||
|
cache = get_cache('django.core.cache.backends.memcached.MemcachedCache',
|
||||||
|
LOCATION='127.0.0.1:11211', TIMEOUT=30,
|
||||||
|
})
|
||||||
|
|
||||||
|
To create a new instance of a cache in settings.CACHES, pass the alias::
|
||||||
|
|
||||||
|
cache = create_cache('default')
|
||||||
|
|
||||||
|
You can also pass extra parameters to override those in settings.CACHES::
|
||||||
|
|
||||||
|
cache = create_cache('default', LOCATION='bar')
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
# We can name a cache from settings.CACHES and update its params
|
||||||
|
try:
|
||||||
|
conf = settings.CACHES[backend]
|
||||||
|
except KeyError:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
params = conf.copy()
|
||||||
|
params.update(params)
|
||||||
|
backend = params.pop('BACKEND')
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# Try to get the CACHES entry for the given backend name first
|
|
||||||
try:
|
|
||||||
conf = settings.CACHES[backend]
|
|
||||||
except KeyError:
|
|
||||||
try:
|
|
||||||
# Trying to import the given backend, in case it's a dotted path
|
|
||||||
import_by_path(backend)
|
|
||||||
except ImproperlyConfigured as e:
|
|
||||||
raise InvalidCacheBackendError("Could not find backend '%s': %s" % (
|
|
||||||
backend, e))
|
|
||||||
location = kwargs.pop('LOCATION', '')
|
|
||||||
params = kwargs
|
|
||||||
else:
|
|
||||||
params = conf.copy()
|
|
||||||
params.update(kwargs)
|
|
||||||
backend = params.pop('BACKEND')
|
|
||||||
location = params.pop('LOCATION', '')
|
|
||||||
backend_cls = import_by_path(backend)
|
backend_cls = import_by_path(backend)
|
||||||
except (AttributeError, ImportError, ImproperlyConfigured) as e:
|
except ImproperlyConfigured as e:
|
||||||
raise InvalidCacheBackendError(
|
raise InvalidCacheBackendError("Could not find backend '%s': %s" % (
|
||||||
"Could not find backend '%s': %s" % (backend, e))
|
backend, e
|
||||||
|
))
|
||||||
|
location = params.pop('LOCATION', '')
|
||||||
cache = backend_cls(location, params)
|
cache = backend_cls(location, params)
|
||||||
# Some caches -- python-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 not implemented in a particular backend
|
# end of a request cycle. If not implemented in a particular backend
|
||||||
|
@ -79,4 +100,54 @@ def get_cache(backend, **kwargs):
|
||||||
signals.request_finished.connect(cache.close)
|
signals.request_finished.connect(cache.close)
|
||||||
return cache
|
return cache
|
||||||
|
|
||||||
cache = get_cache(DEFAULT_CACHE_ALIAS)
|
|
||||||
|
class CacheHandler(object):
|
||||||
|
"""
|
||||||
|
A Cache Handler to manage access to Cache instances.
|
||||||
|
|
||||||
|
Ensures only one instance of each alias exists per thread.
|
||||||
|
"""
|
||||||
|
def __init__(self):
|
||||||
|
self._caches = local()
|
||||||
|
|
||||||
|
def __getitem__(self, alias):
|
||||||
|
try:
|
||||||
|
return getattr(self._caches, alias)
|
||||||
|
except AttributeError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
if alias not in settings.CACHES:
|
||||||
|
raise InvalidCacheBackendError(
|
||||||
|
"Could not find config for '%s' in settings.CACHES" % alias
|
||||||
|
)
|
||||||
|
|
||||||
|
cache = create_cache(alias)
|
||||||
|
setattr(self._caches, alias, cache)
|
||||||
|
|
||||||
|
return cache
|
||||||
|
|
||||||
|
caches = CacheHandler()
|
||||||
|
|
||||||
|
class DefaultCacheProxy(object):
|
||||||
|
"""
|
||||||
|
Proxy access to the default Cache object's attributes.
|
||||||
|
|
||||||
|
This allows the legacy `cache` object to be thread-safe using the new
|
||||||
|
``caches`` API.
|
||||||
|
"""
|
||||||
|
def __getattr__(self, name):
|
||||||
|
return getattr(caches[DEFAULT_CACHE_ALIAS], name)
|
||||||
|
|
||||||
|
def __setattr__(self, name, value):
|
||||||
|
return setattr(caches[DEFAULT_CACHE_ALIAS], name, value)
|
||||||
|
|
||||||
|
def __delattr__(self, name):
|
||||||
|
return delattr(caches[DEFAULT_CACHE_ALIAS], name)
|
||||||
|
|
||||||
|
def __eq__(self, other):
|
||||||
|
return caches[DEFAULT_CACHE_ALIAS] == other
|
||||||
|
|
||||||
|
def __ne__(self, other):
|
||||||
|
return caches[DEFAULT_CACHE_ALIAS] != other
|
||||||
|
|
||||||
|
cache = DefaultCacheProxy()
|
||||||
|
|
|
@ -2,13 +2,13 @@
|
||||||
|
|
||||||
import time
|
import time
|
||||||
import pickle
|
import pickle
|
||||||
from threading import local
|
|
||||||
|
|
||||||
from django.core.cache.backends.base import BaseCache, DEFAULT_TIMEOUT
|
from django.core.cache.backends.base import BaseCache, DEFAULT_TIMEOUT
|
||||||
|
|
||||||
from django.utils import six
|
from django.utils import six
|
||||||
from django.utils.deprecation import RenameMethodsBase
|
from django.utils.deprecation import RenameMethodsBase
|
||||||
from django.utils.encoding import force_str
|
from django.utils.encoding import force_str
|
||||||
|
from django.utils.functional import cached_property
|
||||||
|
|
||||||
|
|
||||||
class BaseMemcachedCacheMethods(RenameMethodsBase):
|
class BaseMemcachedCacheMethods(RenameMethodsBase):
|
||||||
|
@ -177,24 +177,14 @@ class PyLibMCCache(BaseMemcachedCache):
|
||||||
"An implementation of a cache binding using pylibmc"
|
"An implementation of a cache binding using pylibmc"
|
||||||
def __init__(self, server, params):
|
def __init__(self, server, params):
|
||||||
import pylibmc
|
import pylibmc
|
||||||
self._local = local()
|
|
||||||
super(PyLibMCCache, self).__init__(server, params,
|
super(PyLibMCCache, self).__init__(server, params,
|
||||||
library=pylibmc,
|
library=pylibmc,
|
||||||
value_not_found_exception=pylibmc.NotFound)
|
value_not_found_exception=pylibmc.NotFound)
|
||||||
|
|
||||||
@property
|
@cached_property
|
||||||
def _cache(self):
|
def _cache(self):
|
||||||
# PylibMC uses cache options as the 'behaviors' attribute.
|
|
||||||
# It also needs to use threadlocals, because some versions of
|
|
||||||
# PylibMC don't play well with the GIL.
|
|
||||||
client = getattr(self._local, 'client', None)
|
|
||||||
if client:
|
|
||||||
return client
|
|
||||||
|
|
||||||
client = self._lib.Client(self._servers)
|
client = self._lib.Client(self._servers)
|
||||||
if self._options:
|
if self._options:
|
||||||
client.behaviors = self._options
|
client.behaviors = self._options
|
||||||
|
|
||||||
self._local.client = client
|
|
||||||
|
|
||||||
return client
|
return client
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
from optparse import make_option
|
from optparse import make_option
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.core.cache import get_cache
|
from django.core.cache import create_cache
|
||||||
from django.core.cache.backends.db import BaseDatabaseCache
|
from django.core.cache.backends.db import BaseDatabaseCache
|
||||||
from django.core.management.base import BaseCommand, CommandError
|
from django.core.management.base import BaseCommand, CommandError
|
||||||
from django.db import connections, router, transaction, models, DEFAULT_DB_ALIAS
|
from django.db import connections, router, transaction, models, DEFAULT_DB_ALIAS
|
||||||
|
@ -30,7 +30,7 @@ class Command(BaseCommand):
|
||||||
self.create_table(db, tablename)
|
self.create_table(db, tablename)
|
||||||
else:
|
else:
|
||||||
for cache_alias in settings.CACHES:
|
for cache_alias in settings.CACHES:
|
||||||
cache = get_cache(cache_alias)
|
cache = create_cache(cache_alias)
|
||||||
if isinstance(cache, BaseDatabaseCache):
|
if isinstance(cache, BaseDatabaseCache):
|
||||||
self.create_table(db, cache._table)
|
self.create_table(db, cache._table)
|
||||||
|
|
||||||
|
|
|
@ -46,7 +46,7 @@ More details about how the caching works:
|
||||||
import warnings
|
import warnings
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.core.cache import get_cache, DEFAULT_CACHE_ALIAS
|
from django.core.cache import create_cache, caches, DEFAULT_CACHE_ALIAS
|
||||||
from django.utils.cache import get_cache_key, learn_cache_key, patch_response_headers, get_max_age
|
from django.utils.cache import get_cache_key, learn_cache_key, patch_response_headers, get_max_age
|
||||||
|
|
||||||
|
|
||||||
|
@ -64,7 +64,7 @@ class UpdateCacheMiddleware(object):
|
||||||
self.key_prefix = settings.CACHE_MIDDLEWARE_KEY_PREFIX
|
self.key_prefix = settings.CACHE_MIDDLEWARE_KEY_PREFIX
|
||||||
self.cache_anonymous_only = getattr(settings, 'CACHE_MIDDLEWARE_ANONYMOUS_ONLY', False)
|
self.cache_anonymous_only = getattr(settings, 'CACHE_MIDDLEWARE_ANONYMOUS_ONLY', False)
|
||||||
self.cache_alias = settings.CACHE_MIDDLEWARE_ALIAS
|
self.cache_alias = settings.CACHE_MIDDLEWARE_ALIAS
|
||||||
self.cache = get_cache(self.cache_alias)
|
self.cache = caches[self.cache_alias]
|
||||||
|
|
||||||
def _session_accessed(self, request):
|
def _session_accessed(self, request):
|
||||||
try:
|
try:
|
||||||
|
@ -122,10 +122,9 @@ class FetchFromCacheMiddleware(object):
|
||||||
MIDDLEWARE_CLASSES so that it'll get called last during the request phase.
|
MIDDLEWARE_CLASSES so that it'll get called last during the request phase.
|
||||||
"""
|
"""
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.cache_timeout = settings.CACHE_MIDDLEWARE_SECONDS
|
|
||||||
self.key_prefix = settings.CACHE_MIDDLEWARE_KEY_PREFIX
|
self.key_prefix = settings.CACHE_MIDDLEWARE_KEY_PREFIX
|
||||||
self.cache_alias = settings.CACHE_MIDDLEWARE_ALIAS
|
self.cache_alias = settings.CACHE_MIDDLEWARE_ALIAS
|
||||||
self.cache = get_cache(self.cache_alias)
|
self.cache = caches[self.cache_alias]
|
||||||
|
|
||||||
def process_request(self, request):
|
def process_request(self, request):
|
||||||
"""
|
"""
|
||||||
|
@ -169,39 +168,32 @@ class CacheMiddleware(UpdateCacheMiddleware, FetchFromCacheMiddleware):
|
||||||
# we fall back to system defaults. If it is not provided at all,
|
# we fall back to system defaults. If it is not provided at all,
|
||||||
# we need to use middleware defaults.
|
# we need to use middleware defaults.
|
||||||
|
|
||||||
cache_kwargs = {}
|
try:
|
||||||
|
key_prefix = kwargs['key_prefix']
|
||||||
|
if key_prefix is None:
|
||||||
|
key_prefix = ''
|
||||||
|
except KeyError:
|
||||||
|
key_prefix = settings.CACHE_MIDDLEWARE_KEY_PREFIX
|
||||||
|
self.key_prefix = key_prefix
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self.key_prefix = kwargs['key_prefix']
|
cache_alias = kwargs['cache_alias']
|
||||||
if self.key_prefix is not None:
|
if cache_alias is None:
|
||||||
cache_kwargs['KEY_PREFIX'] = self.key_prefix
|
cache_alias = DEFAULT_CACHE_ALIAS
|
||||||
else:
|
|
||||||
self.key_prefix = ''
|
|
||||||
except KeyError:
|
except KeyError:
|
||||||
self.key_prefix = settings.CACHE_MIDDLEWARE_KEY_PREFIX
|
cache_alias = settings.CACHE_MIDDLEWARE_ALIAS
|
||||||
cache_kwargs['KEY_PREFIX'] = self.key_prefix
|
self.cache_alias = cache_alias
|
||||||
|
|
||||||
try:
|
if cache_timeout is None:
|
||||||
self.cache_alias = kwargs['cache_alias']
|
cache_timeout = settings.CACHE_MIDDLEWARE_SECONDS
|
||||||
if self.cache_alias is None:
|
self.cache_timeout = cache_timeout
|
||||||
self.cache_alias = DEFAULT_CACHE_ALIAS
|
|
||||||
if cache_timeout is not None:
|
|
||||||
cache_kwargs['TIMEOUT'] = cache_timeout
|
|
||||||
except KeyError:
|
|
||||||
self.cache_alias = settings.CACHE_MIDDLEWARE_ALIAS
|
|
||||||
if cache_timeout is None:
|
|
||||||
cache_kwargs['TIMEOUT'] = settings.CACHE_MIDDLEWARE_SECONDS
|
|
||||||
else:
|
|
||||||
cache_kwargs['TIMEOUT'] = cache_timeout
|
|
||||||
|
|
||||||
if cache_anonymous_only is None:
|
if cache_anonymous_only is None:
|
||||||
self.cache_anonymous_only = getattr(settings, 'CACHE_MIDDLEWARE_ANONYMOUS_ONLY', False)
|
cache_anonymous_only = getattr(settings, 'CACHE_MIDDLEWARE_ANONYMOUS_ONLY', False)
|
||||||
else:
|
self.cache_anonymous_only = cache_anonymous_only
|
||||||
self.cache_anonymous_only = cache_anonymous_only
|
|
||||||
|
|
||||||
if self.cache_anonymous_only:
|
if self.cache_anonymous_only:
|
||||||
msg = "CACHE_MIDDLEWARE_ANONYMOUS_ONLY has been deprecated and will be removed in Django 1.8."
|
msg = "CACHE_MIDDLEWARE_ANONYMOUS_ONLY has been deprecated and will be removed in Django 1.8."
|
||||||
warnings.warn(msg, DeprecationWarning, stacklevel=1)
|
warnings.warn(msg, DeprecationWarning, stacklevel=1)
|
||||||
|
|
||||||
self.cache = get_cache(self.cache_alias, **cache_kwargs)
|
self.cache = create_cache(self.cache_alias)
|
||||||
self.cache_timeout = self.cache.default_timeout
|
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
from django.core.cache import get_cache, InvalidCacheBackendError
|
from django.core.cache import caches, InvalidCacheBackendError
|
||||||
from django.core.cache.utils import make_template_fragment_key
|
from django.core.cache.utils import make_template_fragment_key
|
||||||
from django.template import Library, Node, TemplateSyntaxError, VariableDoesNotExist
|
from django.template import Library, Node, TemplateSyntaxError, VariableDoesNotExist
|
||||||
|
|
||||||
register = Library()
|
register = Library()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
default_cache = get_cache('template_fragments')
|
default_cache = caches['template_fragments']
|
||||||
except InvalidCacheBackendError:
|
except InvalidCacheBackendError:
|
||||||
from django.core.cache import cache as default_cache
|
from django.core.cache import cache as default_cache
|
||||||
|
|
||||||
|
@ -35,7 +35,7 @@ class CacheNode(Node):
|
||||||
except VariableDoesNotExist:
|
except VariableDoesNotExist:
|
||||||
raise TemplateSyntaxError('"cache" tag got an unknown variable: %r' % self.cache_name.var)
|
raise TemplateSyntaxError('"cache" tag got an unknown variable: %r' % self.cache_name.var)
|
||||||
try:
|
try:
|
||||||
cache = get_cache(cache_name)
|
cache = caches[cache_name]
|
||||||
except InvalidCacheBackendError:
|
except InvalidCacheBackendError:
|
||||||
raise TemplateSyntaxError('Invalid cache name specified for cache tag: %r' % cache_name)
|
raise TemplateSyntaxError('Invalid cache name specified for cache tag: %r' % cache_name)
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -23,7 +23,7 @@ import re
|
||||||
import time
|
import time
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.core.cache import get_cache
|
from django.core.cache import caches
|
||||||
from django.utils.encoding import iri_to_uri, force_bytes, force_text
|
from django.utils.encoding import iri_to_uri, force_bytes, force_text
|
||||||
from django.utils.http import http_date
|
from django.utils.http import http_date
|
||||||
from django.utils.timezone import get_current_timezone_name
|
from django.utils.timezone import get_current_timezone_name
|
||||||
|
@ -219,7 +219,7 @@ def get_cache_key(request, key_prefix=None, method='GET', cache=None):
|
||||||
key_prefix = settings.CACHE_MIDDLEWARE_KEY_PREFIX
|
key_prefix = settings.CACHE_MIDDLEWARE_KEY_PREFIX
|
||||||
cache_key = _generate_cache_header_key(key_prefix, request)
|
cache_key = _generate_cache_header_key(key_prefix, request)
|
||||||
if cache is None:
|
if cache is None:
|
||||||
cache = get_cache(settings.CACHE_MIDDLEWARE_ALIAS)
|
cache = caches[settings.CACHE_MIDDLEWARE_ALIAS]
|
||||||
headerlist = cache.get(cache_key, None)
|
headerlist = cache.get(cache_key, None)
|
||||||
if headerlist is not None:
|
if headerlist is not None:
|
||||||
return _generate_cache_key(request, method, headerlist, key_prefix)
|
return _generate_cache_key(request, method, headerlist, key_prefix)
|
||||||
|
@ -246,7 +246,7 @@ def learn_cache_key(request, response, cache_timeout=None, key_prefix=None, cach
|
||||||
cache_timeout = settings.CACHE_MIDDLEWARE_SECONDS
|
cache_timeout = settings.CACHE_MIDDLEWARE_SECONDS
|
||||||
cache_key = _generate_cache_header_key(key_prefix, request)
|
cache_key = _generate_cache_header_key(key_prefix, request)
|
||||||
if cache is None:
|
if cache is None:
|
||||||
cache = get_cache(settings.CACHE_MIDDLEWARE_ALIAS)
|
cache = caches[settings.CACHE_MIDDLEWARE_ALIAS]
|
||||||
if response.has_header('Vary'):
|
if response.has_header('Vary'):
|
||||||
is_accept_language_redundant = settings.USE_I18N or settings.USE_L10N
|
is_accept_language_redundant = settings.USE_I18N or settings.USE_L10N
|
||||||
# If i18n or l10n are used, the generated cache key will be suffixed
|
# If i18n or l10n are used, the generated cache key will be suffixed
|
||||||
|
|
|
@ -215,6 +215,9 @@ these changes.
|
||||||
|
|
||||||
* The internal ``django.utils.functional.memoize`` will be removed.
|
* The internal ``django.utils.functional.memoize`` will be removed.
|
||||||
|
|
||||||
|
* ``get_cache`` from django.core.cache will be removed. Instead, use
|
||||||
|
``create_cache`` or ``caches``, depending on your need.
|
||||||
|
|
||||||
2.0
|
2.0
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
|
@ -269,6 +269,26 @@ Minor features
|
||||||
allowing the ``published`` element to be included in the feed (which
|
allowing the ``published`` element to be included in the feed (which
|
||||||
relies on ``pubdate``).
|
relies on ``pubdate``).
|
||||||
|
|
||||||
|
Cache
|
||||||
|
^^^^^
|
||||||
|
|
||||||
|
* Access to caches configured in ``settings.CACHES`` is now available via
|
||||||
|
``django.core.cache.caches``. This will now return a different instance per
|
||||||
|
thread.
|
||||||
|
|
||||||
|
* A new function ``django.core.cache.create_cache`` has been added to make it
|
||||||
|
clearer what's happening. ``django.core.cache.get_cache`` will call this
|
||||||
|
if it's passed anything other than just a cache config alias.
|
||||||
|
|
||||||
|
* ``django.core.cache.get_cache`` has been deprecated. Use
|
||||||
|
``django.core.cache.caches`` to access caches configurd in
|
||||||
|
``settings.CACHES``, or ``django.core.cache.create_cache`` to create ad-hoc
|
||||||
|
instances.
|
||||||
|
|
||||||
|
* All thread safety in cache backends has been removed, as
|
||||||
|
``django.core.cache.caches`` now yields differend backend instances per
|
||||||
|
thread.
|
||||||
|
|
||||||
Email
|
Email
|
||||||
^^^^^
|
^^^^^
|
||||||
|
|
||||||
|
@ -643,6 +663,12 @@ Miscellaneous
|
||||||
Features deprecated in 1.7
|
Features deprecated in 1.7
|
||||||
==========================
|
==========================
|
||||||
|
|
||||||
|
``django.core.cache.get_cache``
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
``django.core.cache.get_cache`` has been supplanted by
|
||||||
|
``django.core.cache.caches`` and ``django.core.cache.create_cache``.
|
||||||
|
|
||||||
``django.utils.dictconfig``/``django.utils.importlib``
|
``django.utils.dictconfig``/``django.utils.importlib``
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
|
|
@ -703,7 +703,22 @@ pickling.)
|
||||||
Accessing the cache
|
Accessing the cache
|
||||||
-------------------
|
-------------------
|
||||||
|
|
||||||
.. function:: django.core.cache.get_cache(backend, **kwargs)
|
.. versionadded:: 1.7
|
||||||
|
|
||||||
|
You can access the caches configured in ``settings.CACHES`` through the
|
||||||
|
dict-like ``django.core.cache.caches`` object. Repeated requests for the same
|
||||||
|
alias will return the same object.
|
||||||
|
|
||||||
|
>>> from django.core.cache import caches
|
||||||
|
>>> cache1 = caches['myalias']
|
||||||
|
>>> cache2 = caches['myalias']
|
||||||
|
>>> cache1 is cache2
|
||||||
|
True
|
||||||
|
|
||||||
|
If the named key does not exist, ``InvalidCacheBackendError`` will be raised.
|
||||||
|
|
||||||
|
The ``caches`` dict is thread aware, so a different instance of each alias will
|
||||||
|
be returned for each thread.
|
||||||
|
|
||||||
The cache module, ``django.core.cache``, has a ``cache`` object that's
|
The cache module, ``django.core.cache``, has a ``cache`` object that's
|
||||||
automatically created from the ``'default'`` entry in the :setting:`CACHES`
|
automatically created from the ``'default'`` entry in the :setting:`CACHES`
|
||||||
|
@ -711,13 +726,43 @@ setting::
|
||||||
|
|
||||||
>>> from django.core.cache import cache
|
>>> from django.core.cache import cache
|
||||||
|
|
||||||
If you have multiple caches defined in :setting:`CACHES`, then you can use
|
This is a proxy object to caches['default']. It is provided for backward
|
||||||
:func:`django.core.cache.get_cache` to retrieve a cache object for any key::
|
compatiblity.
|
||||||
|
|
||||||
|
.. function:: django.core.cache.create_cache(backend, **kwargs)
|
||||||
|
|
||||||
|
You can create caches from ad-hoc configurations using ``create_cache``.
|
||||||
|
|
||||||
|
>>> from django.core.cache import create_cache
|
||||||
|
# Create an instance of a specific backend
|
||||||
|
>>> cache = create_cache(
|
||||||
|
'django.core.cache.backends.memcached.MemcachedCache',
|
||||||
|
LOCATION='/tmp/memcached.sock'
|
||||||
|
)
|
||||||
|
# Create a separate copy of the 'default' cache:
|
||||||
|
>>> new_default = create_cache('default')
|
||||||
|
# Create a cache with the same config as 'default', but a different timeout
|
||||||
|
>>> cache2 = create_cache('default', TIMEOUT=1)
|
||||||
|
|
||||||
|
This is guaranteed to always create a new instance.
|
||||||
|
|
||||||
|
.. function:: django.core.cache.get_cache(backend, **kwargs)
|
||||||
|
|
||||||
|
.. deprecated:: 1.7
|
||||||
|
This function has been deprecated in favour of ``caches`` and
|
||||||
|
``create_cache``.
|
||||||
|
|
||||||
|
Before Django 1.7 this was the only way to get a cache instance. Now it acts
|
||||||
|
as a wrapper to ``create_cache``, except in the case where it is passed only a
|
||||||
|
configured alias, where it will return the cache from ``caches``::
|
||||||
|
|
||||||
>>> from django.core.cache import get_cache
|
>>> from django.core.cache import get_cache
|
||||||
>>> cache = get_cache('alternate')
|
# Passes call to create_cache
|
||||||
|
>>> cache = get_cache('django.core.cache.backends.memcached.MemcachedCache', LOCATION='127.0.0.2')
|
||||||
If the named key does not exist, ``InvalidCacheBackendError`` will be raised.
|
# Creates a new cache based on the config in settings.CACHES['default']
|
||||||
|
>>> cache = get_cache('default', TIMEOUT=300)
|
||||||
|
# Returns instance from caches object
|
||||||
|
>>> cache = get_cache('default')
|
||||||
|
|
||||||
|
|
||||||
Basic usage
|
Basic usage
|
||||||
|
|
|
@ -10,13 +10,14 @@ import random
|
||||||
import re
|
import re
|
||||||
import string
|
import string
|
||||||
import tempfile
|
import tempfile
|
||||||
|
import threading
|
||||||
import time
|
import time
|
||||||
import unittest
|
import unittest
|
||||||
import warnings
|
import warnings
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.core import management
|
from django.core import management
|
||||||
from django.core.cache import get_cache
|
from django.core.cache import create_cache, caches
|
||||||
from django.core.cache.backends.base import (CacheKeyWarning,
|
from django.core.cache.backends.base import (CacheKeyWarning,
|
||||||
InvalidCacheBackendError)
|
InvalidCacheBackendError)
|
||||||
from django.db import connection, router, transaction
|
from django.db import connection, router, transaction
|
||||||
|
@ -55,7 +56,7 @@ class DummyCacheTests(unittest.TestCase):
|
||||||
backend_name = 'django.core.cache.backends.dummy.DummyCache'
|
backend_name = 'django.core.cache.backends.dummy.DummyCache'
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.cache = get_cache(self.backend_name)
|
self.cache = create_cache(self.backend_name)
|
||||||
|
|
||||||
def test_simple(self):
|
def test_simple(self):
|
||||||
"Dummy cache backend ignores cache set calls"
|
"Dummy cache backend ignores cache set calls"
|
||||||
|
@ -840,11 +841,11 @@ class DBCacheTests(BaseCacheTests, TransactionTestCase):
|
||||||
# Spaces are used in the table name to ensure quoting/escaping is working
|
# Spaces are used in the table name to ensure quoting/escaping is working
|
||||||
self._table_name = 'test cache table'
|
self._table_name = 'test cache table'
|
||||||
management.call_command('createcachetable', verbosity=0, interactive=False)
|
management.call_command('createcachetable', verbosity=0, interactive=False)
|
||||||
self.cache = get_cache(self.backend_name, LOCATION=self._table_name, OPTIONS={'MAX_ENTRIES': 30})
|
self.cache = create_cache(self.backend_name, LOCATION=self._table_name, OPTIONS={'MAX_ENTRIES': 30})
|
||||||
self.prefix_cache = get_cache(self.backend_name, LOCATION=self._table_name, KEY_PREFIX='cacheprefix')
|
self.prefix_cache = create_cache(self.backend_name, LOCATION=self._table_name, KEY_PREFIX='cacheprefix')
|
||||||
self.v2_cache = get_cache(self.backend_name, LOCATION=self._table_name, VERSION=2)
|
self.v2_cache = create_cache(self.backend_name, LOCATION=self._table_name, VERSION=2)
|
||||||
self.custom_key_cache = get_cache(self.backend_name, LOCATION=self._table_name, KEY_FUNCTION=custom_key_func)
|
self.custom_key_cache = create_cache(self.backend_name, LOCATION=self._table_name, KEY_FUNCTION=custom_key_func)
|
||||||
self.custom_key_cache2 = get_cache(self.backend_name, LOCATION=self._table_name, KEY_FUNCTION='cache.tests.custom_key_func')
|
self.custom_key_cache2 = create_cache(self.backend_name, LOCATION=self._table_name, KEY_FUNCTION='cache.tests.custom_key_func')
|
||||||
|
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
cursor = connection.cursor()
|
cursor = connection.cursor()
|
||||||
|
@ -855,7 +856,7 @@ class DBCacheTests(BaseCacheTests, TransactionTestCase):
|
||||||
self.perform_cull_test(50, 29)
|
self.perform_cull_test(50, 29)
|
||||||
|
|
||||||
def test_zero_cull(self):
|
def test_zero_cull(self):
|
||||||
self.cache = get_cache(self.backend_name, LOCATION=self._table_name, OPTIONS={'MAX_ENTRIES': 30, 'CULL_FREQUENCY': 0})
|
self.cache = create_cache(self.backend_name, LOCATION=self._table_name, OPTIONS={'MAX_ENTRIES': 30, 'CULL_FREQUENCY': 0})
|
||||||
self.perform_cull_test(50, 18)
|
self.perform_cull_test(50, 18)
|
||||||
|
|
||||||
def test_second_call_doesnt_crash(self):
|
def test_second_call_doesnt_crash(self):
|
||||||
|
@ -950,11 +951,11 @@ class LocMemCacheTests(unittest.TestCase, BaseCacheTests):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.factory = RequestFactory()
|
self.factory = RequestFactory()
|
||||||
self.cache = get_cache(self.backend_name, OPTIONS={'MAX_ENTRIES': 30})
|
self.cache = create_cache(self.backend_name, OPTIONS={'MAX_ENTRIES': 30})
|
||||||
self.prefix_cache = get_cache(self.backend_name, KEY_PREFIX='cacheprefix')
|
self.prefix_cache = create_cache(self.backend_name, KEY_PREFIX='cacheprefix')
|
||||||
self.v2_cache = get_cache(self.backend_name, VERSION=2)
|
self.v2_cache = create_cache(self.backend_name, VERSION=2)
|
||||||
self.custom_key_cache = get_cache(self.backend_name, OPTIONS={'MAX_ENTRIES': 30}, KEY_FUNCTION=custom_key_func)
|
self.custom_key_cache = create_cache(self.backend_name, OPTIONS={'MAX_ENTRIES': 30}, KEY_FUNCTION=custom_key_func)
|
||||||
self.custom_key_cache2 = get_cache(self.backend_name, OPTIONS={'MAX_ENTRIES': 30}, KEY_FUNCTION='cache.tests.custom_key_func')
|
self.custom_key_cache2 = create_cache(self.backend_name, OPTIONS={'MAX_ENTRIES': 30}, KEY_FUNCTION='cache.tests.custom_key_func')
|
||||||
|
|
||||||
# LocMem requires a hack to make the other caches
|
# LocMem requires a hack to make the other caches
|
||||||
# share a data store with the 'normal' cache.
|
# share a data store with the 'normal' cache.
|
||||||
|
@ -977,13 +978,13 @@ class LocMemCacheTests(unittest.TestCase, BaseCacheTests):
|
||||||
self.perform_cull_test(50, 29)
|
self.perform_cull_test(50, 29)
|
||||||
|
|
||||||
def test_zero_cull(self):
|
def test_zero_cull(self):
|
||||||
self.cache = get_cache(self.backend_name, OPTIONS={'MAX_ENTRIES': 30, 'CULL_FREQUENCY': 0})
|
self.cache = create_cache(self.backend_name, OPTIONS={'MAX_ENTRIES': 30, 'CULL_FREQUENCY': 0})
|
||||||
self.perform_cull_test(50, 19)
|
self.perform_cull_test(50, 19)
|
||||||
|
|
||||||
def test_multiple_caches(self):
|
def test_multiple_caches(self):
|
||||||
"Check that multiple locmem caches are isolated"
|
"Check that multiple locmem caches are isolated"
|
||||||
mirror_cache = get_cache(self.backend_name)
|
mirror_cache = create_cache(self.backend_name)
|
||||||
other_cache = get_cache(self.backend_name, LOCATION='other')
|
other_cache = create_cache(self.backend_name, LOCATION='other')
|
||||||
|
|
||||||
self.cache.set('value1', 42)
|
self.cache.set('value1', 42)
|
||||||
self.assertEqual(mirror_cache.get('value1'), 42)
|
self.assertEqual(mirror_cache.get('value1'), 42)
|
||||||
|
@ -1017,11 +1018,11 @@ class MemcachedCacheTests(unittest.TestCase, BaseCacheTests):
|
||||||
if cache['BACKEND'].startswith('django.core.cache.backends.memcached.'):
|
if cache['BACKEND'].startswith('django.core.cache.backends.memcached.'):
|
||||||
break
|
break
|
||||||
random_prefix = ''.join(random.choice(string.ascii_letters) for x in range(10))
|
random_prefix = ''.join(random.choice(string.ascii_letters) for x in range(10))
|
||||||
self.cache = get_cache(cache_key)
|
self.cache = caches[cache_key]
|
||||||
self.prefix_cache = get_cache(cache_key, KEY_PREFIX=random_prefix)
|
self.prefix_cache = create_cache(cache_key, KEY_PREFIX=random_prefix)
|
||||||
self.v2_cache = get_cache(cache_key, VERSION=2)
|
self.v2_cache = create_cache(cache_key, VERSION=2)
|
||||||
self.custom_key_cache = get_cache(cache_key, KEY_FUNCTION=custom_key_func)
|
self.custom_key_cache = create_cache(cache_key, KEY_FUNCTION=custom_key_func)
|
||||||
self.custom_key_cache2 = get_cache(cache_key, KEY_FUNCTION='cache.tests.custom_key_func')
|
self.custom_key_cache2 = create_cache(cache_key, KEY_FUNCTION='cache.tests.custom_key_func')
|
||||||
|
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
self.cache.clear()
|
self.cache.clear()
|
||||||
|
@ -1050,7 +1051,7 @@ class MemcachedCacheTests(unittest.TestCase, BaseCacheTests):
|
||||||
# Regression test for #19810
|
# Regression test for #19810
|
||||||
for cache_key, cache in settings.CACHES.items():
|
for cache_key, cache in settings.CACHES.items():
|
||||||
if cache['BACKEND'] == 'django.core.cache.backends.memcached.MemcachedCache':
|
if cache['BACKEND'] == 'django.core.cache.backends.memcached.MemcachedCache':
|
||||||
self.assertEqual(get_cache(cache_key)._cache.pickleProtocol,
|
self.assertEqual(caches[cache_key]._cache.pickleProtocol,
|
||||||
pickle.HIGHEST_PROTOCOL)
|
pickle.HIGHEST_PROTOCOL)
|
||||||
|
|
||||||
|
|
||||||
|
@ -1063,11 +1064,11 @@ class FileBasedCacheTests(unittest.TestCase, BaseCacheTests):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.factory = RequestFactory()
|
self.factory = RequestFactory()
|
||||||
self.dirname = tempfile.mkdtemp()
|
self.dirname = tempfile.mkdtemp()
|
||||||
self.cache = get_cache(self.backend_name, LOCATION=self.dirname, OPTIONS={'MAX_ENTRIES': 30})
|
self.cache = create_cache(self.backend_name, LOCATION=self.dirname, OPTIONS={'MAX_ENTRIES': 30})
|
||||||
self.prefix_cache = get_cache(self.backend_name, LOCATION=self.dirname, KEY_PREFIX='cacheprefix')
|
self.prefix_cache = create_cache(self.backend_name, LOCATION=self.dirname, KEY_PREFIX='cacheprefix')
|
||||||
self.v2_cache = get_cache(self.backend_name, LOCATION=self.dirname, VERSION=2)
|
self.v2_cache = create_cache(self.backend_name, LOCATION=self.dirname, VERSION=2)
|
||||||
self.custom_key_cache = get_cache(self.backend_name, LOCATION=self.dirname, KEY_FUNCTION=custom_key_func)
|
self.custom_key_cache = create_cache(self.backend_name, LOCATION=self.dirname, KEY_FUNCTION=custom_key_func)
|
||||||
self.custom_key_cache2 = get_cache(self.backend_name, LOCATION=self.dirname, KEY_FUNCTION='cache.tests.custom_key_func')
|
self.custom_key_cache2 = create_cache(self.backend_name, LOCATION=self.dirname, KEY_FUNCTION='cache.tests.custom_key_func')
|
||||||
|
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
self.cache.clear()
|
self.cache.clear()
|
||||||
|
@ -1097,7 +1098,7 @@ class FileBasedCacheTests(unittest.TestCase, BaseCacheTests):
|
||||||
|
|
||||||
def test_zero_cull(self):
|
def test_zero_cull(self):
|
||||||
# Regression test for #15806
|
# Regression test for #15806
|
||||||
self.cache = get_cache(self.backend_name, LOCATION=self.dirname, OPTIONS={'MAX_ENTRIES': 30, 'CULL_FREQUENCY': 0})
|
self.cache = create_cache(self.backend_name, LOCATION=self.dirname, OPTIONS={'MAX_ENTRIES': 30, 'CULL_FREQUENCY': 0})
|
||||||
self.perform_cull_test(50, 19)
|
self.perform_cull_test(50, 19)
|
||||||
|
|
||||||
|
|
||||||
|
@ -1109,7 +1110,7 @@ class CustomCacheKeyValidationTests(unittest.TestCase):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
def test_custom_key_validation(self):
|
def test_custom_key_validation(self):
|
||||||
cache = get_cache('cache.liberal_backend.CacheClass')
|
cache = create_cache('cache.liberal_backend.CacheClass')
|
||||||
|
|
||||||
# this key is both longer than 250 characters, and has spaces
|
# this key is both longer than 250 characters, and has spaces
|
||||||
key = 'some key with spaces' * 15
|
key = 'some key with spaces' * 15
|
||||||
|
@ -1121,18 +1122,23 @@ class CustomCacheKeyValidationTests(unittest.TestCase):
|
||||||
class GetCacheTests(unittest.TestCase):
|
class GetCacheTests(unittest.TestCase):
|
||||||
|
|
||||||
def test_simple(self):
|
def test_simple(self):
|
||||||
from django.core.cache import cache
|
from django.core.cache import caches, DEFAULT_CACHE_ALIAS
|
||||||
self.assertIsInstance(cache, get_cache('default').__class__)
|
self.assertIsInstance(
|
||||||
|
caches[DEFAULT_CACHE_ALIAS],
|
||||||
|
create_cache('default').__class__
|
||||||
|
)
|
||||||
|
|
||||||
cache = get_cache(
|
cache = create_cache(
|
||||||
'django.core.cache.backends.dummy.DummyCache', **{'TIMEOUT': 120})
|
'django.core.cache.backends.dummy.DummyCache',
|
||||||
|
**{'TIMEOUT': 120}
|
||||||
|
)
|
||||||
self.assertEqual(cache.default_timeout, 120)
|
self.assertEqual(cache.default_timeout, 120)
|
||||||
|
|
||||||
self.assertRaises(InvalidCacheBackendError, get_cache, 'does_not_exist')
|
self.assertRaises(InvalidCacheBackendError, create_cache, 'does_not_exist')
|
||||||
|
|
||||||
def test_close(self):
|
def test_close(self):
|
||||||
from django.core import signals
|
from django.core import signals
|
||||||
cache = get_cache('cache.closeable_cache.CacheClass')
|
cache = create_cache('cache.closeable_cache.CacheClass')
|
||||||
self.assertFalse(cache.closed)
|
self.assertFalse(cache.closed)
|
||||||
signals.request_finished.send(self.__class__)
|
signals.request_finished.send(self.__class__)
|
||||||
self.assertTrue(cache.closed)
|
self.assertTrue(cache.closed)
|
||||||
|
@ -1153,7 +1159,7 @@ class CacheUtils(TestCase):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.path = '/cache/test/'
|
self.path = '/cache/test/'
|
||||||
self.cache = get_cache('default')
|
self.cache = caches['default']
|
||||||
self.factory = RequestFactory()
|
self.factory = RequestFactory()
|
||||||
|
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
|
@ -1261,7 +1267,7 @@ class CacheHEADTest(TestCase):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.path = '/cache/test/'
|
self.path = '/cache/test/'
|
||||||
self.cache = get_cache('default')
|
self.cache = caches['default']
|
||||||
self.factory = RequestFactory()
|
self.factory = RequestFactory()
|
||||||
|
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
|
@ -1314,7 +1320,7 @@ class CacheI18nTest(TestCase):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.path = '/cache/test/'
|
self.path = '/cache/test/'
|
||||||
self.cache = get_cache('default')
|
self.cache = create_cache('default')
|
||||||
self.factory = RequestFactory()
|
self.factory = RequestFactory()
|
||||||
|
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
|
@ -1581,8 +1587,8 @@ class CacheMiddlewareTest(IgnoreDeprecationWarningsMixin, TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(CacheMiddlewareTest, self).setUp()
|
super(CacheMiddlewareTest, self).setUp()
|
||||||
self.factory = RequestFactory()
|
self.factory = RequestFactory()
|
||||||
self.default_cache = get_cache('default')
|
self.default_cache = create_cache('default')
|
||||||
self.other_cache = get_cache('other')
|
self.other_cache = create_cache('other')
|
||||||
|
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
self.default_cache.clear()
|
self.default_cache.clear()
|
||||||
|
@ -1608,7 +1614,7 @@ class CacheMiddlewareTest(IgnoreDeprecationWarningsMixin, TestCase):
|
||||||
# First, test with "defaults":
|
# First, test with "defaults":
|
||||||
as_view_decorator = CacheMiddleware(cache_alias=None, key_prefix=None)
|
as_view_decorator = CacheMiddleware(cache_alias=None, key_prefix=None)
|
||||||
|
|
||||||
self.assertEqual(as_view_decorator.cache_timeout, 300) # Timeout value for 'default' cache, i.e. 300
|
self.assertEqual(as_view_decorator.cache_timeout, 30) # Timeout value for 'default' cache, i.e. 30
|
||||||
self.assertEqual(as_view_decorator.key_prefix, '')
|
self.assertEqual(as_view_decorator.key_prefix, '')
|
||||||
self.assertEqual(as_view_decorator.cache_alias, 'default') # Value of DEFAULT_CACHE_ALIAS from django.core.cache
|
self.assertEqual(as_view_decorator.cache_alias, 'default') # Value of DEFAULT_CACHE_ALIAS from django.core.cache
|
||||||
self.assertEqual(as_view_decorator.cache_anonymous_only, False)
|
self.assertEqual(as_view_decorator.cache_anonymous_only, False)
|
||||||
|
@ -1755,7 +1761,7 @@ class CacheMiddlewareTest(IgnoreDeprecationWarningsMixin, TestCase):
|
||||||
time.sleep(2)
|
time.sleep(2)
|
||||||
|
|
||||||
# ... the default cache will still hit
|
# ... the default cache will still hit
|
||||||
get_cache('default')
|
caches['default']
|
||||||
response = default_view(request, '11')
|
response = default_view(request, '11')
|
||||||
self.assertEqual(response.content, b'Hello World 1')
|
self.assertEqual(response.content, b'Hello World 1')
|
||||||
|
|
||||||
|
@ -1801,7 +1807,7 @@ class TestWithTemplateResponse(TestCase):
|
||||||
"""
|
"""
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.path = '/cache/test/'
|
self.path = '/cache/test/'
|
||||||
self.cache = get_cache('default')
|
self.cache = create_cache('default')
|
||||||
self.factory = RequestFactory()
|
self.factory = RequestFactory()
|
||||||
|
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
|
@ -1904,3 +1910,29 @@ class TestMakeTemplateFragmentKey(TestCase):
|
||||||
key = make_template_fragment_key('spam', ['abc:def%'])
|
key = make_template_fragment_key('spam', ['abc:def%'])
|
||||||
self.assertEqual(key,
|
self.assertEqual(key,
|
||||||
'template.cache.spam.f27688177baec990cdf3fbd9d9c3f469')
|
'template.cache.spam.f27688177baec990cdf3fbd9d9c3f469')
|
||||||
|
|
||||||
|
class CacheHandlerTest(TestCase):
|
||||||
|
def test_same_instance(self):
|
||||||
|
"""
|
||||||
|
Attempting to retrieve the same alias should yield the same instance.
|
||||||
|
"""
|
||||||
|
cache1 = caches['default']
|
||||||
|
cache2 = caches['default']
|
||||||
|
|
||||||
|
self.assertTrue(cache1 is cache2)
|
||||||
|
|
||||||
|
def test_per_thread(self):
|
||||||
|
"""
|
||||||
|
Requesting the same alias from separate threads should yield separate
|
||||||
|
instances.
|
||||||
|
"""
|
||||||
|
c = []
|
||||||
|
def runner():
|
||||||
|
c.append(caches['default'])
|
||||||
|
|
||||||
|
for x in range(2):
|
||||||
|
t = threading.Thread(target=runner)
|
||||||
|
t.start()
|
||||||
|
t.join()
|
||||||
|
|
||||||
|
self.assertFalse(c[0] is c[1])
|
||||||
|
|
Loading…
Reference in New Issue