| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145 |
- from django.conf import settings
- from django.contrib.sessions.backends.base import CreateError, SessionBase, UpdateError
- from django.core.cache import caches
- KEY_PREFIX = "django.contrib.sessions.cache"
- class SessionStore(SessionBase):
- """
- A cache-based session store.
- """
- cache_key_prefix = KEY_PREFIX
- def __init__(self, session_key=None):
- self._cache = caches[settings.SESSION_CACHE_ALIAS]
- super().__init__(session_key)
- @property
- def cache_key(self):
- return self.cache_key_prefix + self._get_or_create_session_key()
- async def acache_key(self):
- return self.cache_key_prefix + await self._aget_or_create_session_key()
- def load(self):
- try:
- session_data = self._cache.get(self.cache_key)
- except Exception:
- # Some backends (e.g. memcache) raise an exception on invalid
- # cache keys. If this happens, reset the session. See #17810.
- session_data = None
- if session_data is not None:
- return session_data
- self._session_key = None
- return {}
- async def aload(self):
- try:
- session_data = await self._cache.aget(await self.acache_key())
- except Exception:
- session_data = None
- if session_data is not None:
- return session_data
- self._session_key = None
- return {}
- def create(self):
- # Because a cache can fail silently (e.g. memcache), we don't know if
- # we are failing to create a new session because of a key collision or
- # because the cache is missing. So we try for a (large) number of times
- # and then raise an exception. That's the risk you shoulder if using
- # cache backing.
- for i in range(10000):
- self._session_key = self._get_new_session_key()
- try:
- self.save(must_create=True)
- except CreateError:
- continue
- self.modified = True
- return
- raise RuntimeError(
- "Unable to create a new session key. "
- "It is likely that the cache is unavailable."
- )
- async def acreate(self):
- for i in range(10000):
- self._session_key = await self._aget_new_session_key()
- try:
- await self.asave(must_create=True)
- except CreateError:
- continue
- self.modified = True
- return
- raise RuntimeError(
- "Unable to create a new session key. "
- "It is likely that the cache is unavailable."
- )
- def save(self, must_create=False):
- if self.session_key is None:
- return self.create()
- if must_create:
- func = self._cache.add
- elif self._cache.get(self.cache_key) is not None:
- func = self._cache.set
- else:
- raise UpdateError
- result = func(
- self.cache_key,
- self._get_session(no_load=must_create),
- self.get_expiry_age(),
- )
- if must_create and not result:
- raise CreateError
- async def asave(self, must_create=False):
- if self.session_key is None:
- return await self.acreate()
- if must_create:
- func = self._cache.aadd
- elif await self._cache.aget(await self.acache_key()) is not None:
- func = self._cache.aset
- else:
- raise UpdateError
- result = await func(
- await self.acache_key(),
- await self._aget_session(no_load=must_create),
- await self.aget_expiry_age(),
- )
- if must_create and not result:
- raise CreateError
- def exists(self, session_key):
- return (
- bool(session_key) and (self.cache_key_prefix + session_key) in self._cache
- )
- async def aexists(self, session_key):
- return bool(session_key) and await self._cache.ahas_key(
- self.cache_key_prefix + session_key
- )
- def delete(self, session_key=None):
- if session_key is None:
- if self.session_key is None:
- return
- session_key = self.session_key
- self._cache.delete(self.cache_key_prefix + session_key)
- async def adelete(self, session_key=None):
- if session_key is None:
- if self.session_key is None:
- return
- session_key = self.session_key
- await self._cache.adelete(self.cache_key_prefix + session_key)
- @classmethod
- def clear_expired(cls):
- pass
- @classmethod
- async def aclear_expired(cls):
- pass
|