对于在2023年尝试此操作的任何人的更新:接受的答案是一个很好的起点,但是不完整。
google.appengine.api.memcache
接口和
django.core.cache.backends.memcached.BaseMemcachedCache
接口之间存在一些不一致之处,需要解决。
我正在使用Django 3.2在Google App Engine Python 3.10运行时。
对于我的使用情况,最重要的不一致之处是:
GAE的`memcache.get`不支持`BaseMemcachedCache.get`的`default`参数。这会导致一个令人困惑的`TypeError: Cannot set google.appengine.MemcacheGetRequest.name_space`错误。
Google的`memcache.incr`和`memcache.decr`支持一个`initial_value`参数,而`BaseMemcachedCache.incr/decr`不支持。
与(1)相关的一个重要推论是,GAE的`memcache`不支持默认对象模式,用于区分缓存未命中和合法缓存的`None`值。
下面的实现解决了我的使用情况中的这些不一致性。我相信GAE的`memcache` API和`BaseMemcachedCache`之间还存在其他不一致性,但它们目前与我的使用情况无关。请随意将此作为您自己实现的起点。
from google.appengine.api import memcache
from django.utils.functional import cached_property
from django.core.cache.backends.base import DEFAULT_TIMEOUT
from django.core.cache.backends.memcached import BaseMemcachedCache
class GaeMemcachedCache(BaseMemcachedCache):
def __init__(self, server, params):
super().__init__(server, params, library=memcache, value_not_found_exception=ValueError)
def decr(self, key, delta=1, version=None, initial_value=None):
if delta < 0:
return self.incr(key, delta=-delta, version=version, initial_value=initial_value)
key = self.make_key(key, version=version)
self.validate_key(key)
value = self._cache.decr(key, delta=delta, initial_value=initial_value)
if value is None:
raise ValueError(f"Key '{key}' not found")
return value
def get(self, key, default=None, version=None):
if not self.has_key(key, version=version):
return default
key = self.make_key(key, version=version)
self.validate_key(key)
return self._cache.get(key)
def has_key(self, key, version=None):
"""
Returns `True` if a key is present in the cache, otherwise `False`.
`google.appengine.api.memcache.get` doesn't support a `default` argument. Instead, it
returns `None` for a cache miss. `replace()`, however, returns False if the intended
key isn't present in the cache. We can therefore check to see if key is present by
`get`ing it and then replacing it with itself.
`google.appengine.api.memcache` also doesn't support a way to determine a key's
expiration time, so this approach results in the loss of that information, but
only for keys whose value was positively set to `None`.
"""
key = self.make_key(key, version=version)
self.validate_key(key)
value = self._cache.get(key)
if value is None:
return self._cache.replace(key, value)
return True
def incr(self, key, delta=1, version=None, initial_value=None):
if delta < 0:
return self.decr(key, delta=-delta, version=version, initial_value=initial_value)
key = self.make_key(key, version=version)
self.validate_key(key)
value = self._cache.incr(key, delta=delta, initial_value=initial_value)
if value is None:
raise ValueError(f"Key '{key}' not found")
return value
@cached_property
def _cache(self):
if getattr(self, '_client', None) is None:
self._client = self._lib._CLIENT
return self._client