mirror of
https://github.com/django/django.git
synced 2025-11-07 07:15:35 +00:00
Add warning when using cache keys that might not work with memcached.
This means testing with local dev caches (not memcache) will warn developers if they are introducing inadvertent importabilities. There is also the ability to silence the warning if a dev is not planning to use memcache and knows what they are doing with their keys. Thanks to Carl Meyer for the patch. Fixed #6447. git-svn-id: http://code.djangoproject.com/svn/django/trunk@13766 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
2
django/core/cache/__init__.py
vendored
2
django/core/cache/__init__.py
vendored
@@ -18,7 +18,7 @@ See docs/cache.txt for information on the public API.
|
||||
from cgi import parse_qsl
|
||||
from django.conf import settings
|
||||
from django.core import signals
|
||||
from django.core.cache.backends.base import InvalidCacheBackendError
|
||||
from django.core.cache.backends.base import InvalidCacheBackendError, CacheKeyWarning
|
||||
from django.utils import importlib
|
||||
|
||||
# Name for use in settings file --> name of module in "backends" directory.
|
||||
|
||||
28
django/core/cache/backends/base.py
vendored
28
django/core/cache/backends/base.py
vendored
@@ -1,10 +1,18 @@
|
||||
"Base Cache class."
|
||||
|
||||
from django.core.exceptions import ImproperlyConfigured
|
||||
import warnings
|
||||
|
||||
from django.core.exceptions import ImproperlyConfigured, DjangoRuntimeWarning
|
||||
|
||||
class InvalidCacheBackendError(ImproperlyConfigured):
|
||||
pass
|
||||
|
||||
class CacheKeyWarning(DjangoRuntimeWarning):
|
||||
pass
|
||||
|
||||
# Memcached does not accept keys longer than this.
|
||||
MEMCACHE_MAX_KEY_LENGTH = 250
|
||||
|
||||
class BaseCache(object):
|
||||
def __init__(self, params):
|
||||
timeout = params.get('timeout', 300)
|
||||
@@ -116,3 +124,21 @@ class BaseCache(object):
|
||||
def clear(self):
|
||||
"""Remove *all* values from the cache at once."""
|
||||
raise NotImplementedError
|
||||
|
||||
def validate_key(self, key):
|
||||
"""
|
||||
Warn about keys that would not be portable to the memcached
|
||||
backend. This encourages (but does not force) writing backend-portable
|
||||
cache code.
|
||||
|
||||
"""
|
||||
if len(key) > MEMCACHE_MAX_KEY_LENGTH:
|
||||
warnings.warn('Cache key will cause errors if used with memcached: '
|
||||
'%s (longer than %s)' % (key, MEMCACHE_MAX_KEY_LENGTH),
|
||||
CacheKeyWarning)
|
||||
for char in key:
|
||||
if ord(char) < 33 or ord(char) == 127:
|
||||
warnings.warn('Cache key contains characters that will cause '
|
||||
'errors if used with memcached: %r' % key,
|
||||
CacheKeyWarning)
|
||||
|
||||
|
||||
5
django/core/cache/backends/db.py
vendored
5
django/core/cache/backends/db.py
vendored
@@ -46,6 +46,7 @@ class CacheClass(BaseCache):
|
||||
self._cull_frequency = 3
|
||||
|
||||
def get(self, key, default=None):
|
||||
self.validate_key(key)
|
||||
db = router.db_for_read(self.cache_model_class)
|
||||
table = connections[db].ops.quote_name(self._table)
|
||||
cursor = connections[db].cursor()
|
||||
@@ -65,9 +66,11 @@ class CacheClass(BaseCache):
|
||||
return pickle.loads(base64.decodestring(value))
|
||||
|
||||
def set(self, key, value, timeout=None):
|
||||
self.validate_key(key)
|
||||
self._base_set('set', key, value, timeout)
|
||||
|
||||
def add(self, key, value, timeout=None):
|
||||
self.validate_key(key)
|
||||
return self._base_set('add', key, value, timeout)
|
||||
|
||||
def _base_set(self, mode, key, value, timeout=None):
|
||||
@@ -103,6 +106,7 @@ class CacheClass(BaseCache):
|
||||
return True
|
||||
|
||||
def delete(self, key):
|
||||
self.validate_key(key)
|
||||
db = router.db_for_write(self.cache_model_class)
|
||||
table = connections[db].ops.quote_name(self._table)
|
||||
cursor = connections[db].cursor()
|
||||
@@ -111,6 +115,7 @@ class CacheClass(BaseCache):
|
||||
transaction.commit_unless_managed(using=db)
|
||||
|
||||
def has_key(self, key):
|
||||
self.validate_key(key)
|
||||
db = router.db_for_read(self.cache_model_class)
|
||||
table = connections[db].ops.quote_name(self._table)
|
||||
cursor = connections[db].cursor()
|
||||
|
||||
15
django/core/cache/backends/dummy.py
vendored
15
django/core/cache/backends/dummy.py
vendored
@@ -6,22 +6,25 @@ class CacheClass(BaseCache):
|
||||
def __init__(self, *args, **kwargs):
|
||||
pass
|
||||
|
||||
def add(self, *args, **kwargs):
|
||||
def add(self, key, *args, **kwargs):
|
||||
self.validate_key(key)
|
||||
return True
|
||||
|
||||
def get(self, key, default=None):
|
||||
self.validate_key(key)
|
||||
return default
|
||||
|
||||
def set(self, *args, **kwargs):
|
||||
pass
|
||||
def set(self, key, *args, **kwargs):
|
||||
self.validate_key(key)
|
||||
|
||||
def delete(self, *args, **kwargs):
|
||||
pass
|
||||
def delete(self, key, *args, **kwargs):
|
||||
self.validate_key(key)
|
||||
|
||||
def get_many(self, *args, **kwargs):
|
||||
return {}
|
||||
|
||||
def has_key(self, *args, **kwargs):
|
||||
def has_key(self, key, *args, **kwargs):
|
||||
self.validate_key(key)
|
||||
return False
|
||||
|
||||
def set_many(self, *args, **kwargs):
|
||||
|
||||
5
django/core/cache/backends/filebased.py
vendored
5
django/core/cache/backends/filebased.py
vendored
@@ -32,6 +32,7 @@ class CacheClass(BaseCache):
|
||||
self._createdir()
|
||||
|
||||
def add(self, key, value, timeout=None):
|
||||
self.validate_key(key)
|
||||
if self.has_key(key):
|
||||
return False
|
||||
|
||||
@@ -39,6 +40,7 @@ class CacheClass(BaseCache):
|
||||
return True
|
||||
|
||||
def get(self, key, default=None):
|
||||
self.validate_key(key)
|
||||
fname = self._key_to_file(key)
|
||||
try:
|
||||
f = open(fname, 'rb')
|
||||
@@ -56,6 +58,7 @@ class CacheClass(BaseCache):
|
||||
return default
|
||||
|
||||
def set(self, key, value, timeout=None):
|
||||
self.validate_key(key)
|
||||
fname = self._key_to_file(key)
|
||||
dirname = os.path.dirname(fname)
|
||||
|
||||
@@ -79,6 +82,7 @@ class CacheClass(BaseCache):
|
||||
pass
|
||||
|
||||
def delete(self, key):
|
||||
self.validate_key(key)
|
||||
try:
|
||||
self._delete(self._key_to_file(key))
|
||||
except (IOError, OSError):
|
||||
@@ -95,6 +99,7 @@ class CacheClass(BaseCache):
|
||||
pass
|
||||
|
||||
def has_key(self, key):
|
||||
self.validate_key(key)
|
||||
fname = self._key_to_file(key)
|
||||
try:
|
||||
f = open(fname, 'rb')
|
||||
|
||||
5
django/core/cache/backends/locmem.py
vendored
5
django/core/cache/backends/locmem.py
vendored
@@ -30,6 +30,7 @@ class CacheClass(BaseCache):
|
||||
self._lock = RWLock()
|
||||
|
||||
def add(self, key, value, timeout=None):
|
||||
self.validate_key(key)
|
||||
self._lock.writer_enters()
|
||||
try:
|
||||
exp = self._expire_info.get(key)
|
||||
@@ -44,6 +45,7 @@ class CacheClass(BaseCache):
|
||||
self._lock.writer_leaves()
|
||||
|
||||
def get(self, key, default=None):
|
||||
self.validate_key(key)
|
||||
self._lock.reader_enters()
|
||||
try:
|
||||
exp = self._expire_info.get(key)
|
||||
@@ -76,6 +78,7 @@ class CacheClass(BaseCache):
|
||||
self._expire_info[key] = time.time() + timeout
|
||||
|
||||
def set(self, key, value, timeout=None):
|
||||
self.validate_key(key)
|
||||
self._lock.writer_enters()
|
||||
# Python 2.4 doesn't allow combined try-except-finally blocks.
|
||||
try:
|
||||
@@ -87,6 +90,7 @@ class CacheClass(BaseCache):
|
||||
self._lock.writer_leaves()
|
||||
|
||||
def has_key(self, key):
|
||||
self.validate_key(key)
|
||||
self._lock.reader_enters()
|
||||
try:
|
||||
exp = self._expire_info.get(key)
|
||||
@@ -127,6 +131,7 @@ class CacheClass(BaseCache):
|
||||
pass
|
||||
|
||||
def delete(self, key):
|
||||
self.validate_key(key)
|
||||
self._lock.writer_enters()
|
||||
try:
|
||||
self._delete(key)
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
"Global Django exceptions"
|
||||
"""
|
||||
Global Django exception and warning classes.
|
||||
"""
|
||||
|
||||
class DjangoRuntimeWarning(RuntimeWarning):
|
||||
pass
|
||||
|
||||
class ObjectDoesNotExist(Exception):
|
||||
"The requested object does not exist"
|
||||
|
||||
Reference in New Issue
Block a user