summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorÉtienne Loks <etienne.loks@iggdrasil.net>2021-10-05 11:59:28 +0200
committerÉtienne Loks <etienne.loks@iggdrasil.net>2021-10-05 11:59:28 +0200
commitc7d4b5bf5e444f3d9122fe5f7a0af6fc94c9b5a8 (patch)
tree1f05f4afb4a68620d56d9aec00431e5fb2b5667d
parent6c18a8690cb914e43e04a50fdaf361f6cc0bddb8 (diff)
downloadIshtar-c7d4b5bf5e444f3d9122fe5f7a0af6fc94c9b5a8.tar.bz2
Ishtar-c7d4b5bf5e444f3d9122fe5f7a0af6fc94c9b5a8.zip
Middleware for per request cacheWIP/cache
-rw-r--r--ishtar_common/middleware.py123
1 files changed, 123 insertions, 0 deletions
diff --git a/ishtar_common/middleware.py b/ishtar_common/middleware.py
new file mode 100644
index 000000000..2782e0612
--- /dev/null
+++ b/ishtar_common/middleware.py
@@ -0,0 +1,123 @@
+
+from django.middleware.csrf import get_token
+
+from collections import OrderedDict
+from threading import Lock
+
+from django.core.cache.backends.base import BaseCache
+from django.core.cache.backends.locmem import LocMemCache
+
+
+import threading
+
+_thread_locals = threading.local()
+
+def get_current_request():
+ return getattr(_thread_locals, 'request', None)
+
+def reset_current_request():
+ setattr(_thread_locals, 'request', None)
+
+
+class RequestCache(LocMemCache):
+ """
+ RequestCache is a customized LocMemCache which stores its data cache as an instance attribute, rather than
+ a global. It's designed to live only as long as the request object that RequestCacheMiddleware attaches it to.
+ """
+
+ def __init__(self):
+ # We explicitly do not call super() here, because while we want BaseCache.__init__() to run, we *don't*
+ # want LocMemCache.__init__() to run, because that would store our caches in its globals.
+ BaseCache.__init__(self, params={})
+
+ self._cache = OrderedDict()
+ self._expire_info = {}
+ self._lock = Lock()
+
+
+def request_cache_middleware(get_response):
+ def middleware(request):
+ get_token(request) # force CSRFTOKEN setup
+ _thread_locals.request = request
+ request.cache = RequestCache()
+ response = get_response(request)
+ reset_current_request()
+ return response
+
+ return middleware
+
+
+def get_request_cache():
+ """
+ Return the current requests cache
+ :return:
+ """
+ return getattr(get_current_request(), "cache", None)
+
+
+# marker for separating args from kwargs (needs to be global)
+cache_args_kwargs_marker = object()
+
+
+def cache_calculate_key(*args, **kwargs):
+ """
+ Calculate the cache key of a function call with args and kwargs
+ Taken from lru_cache
+ :param args:
+ :param kwargs:
+ :return: the calculated key for the function call
+ :rtype: basestring
+ """
+ # combine args with kwargs, separated by the cache_args_kwargs_marker
+ key = args + (cache_args_kwargs_marker,) + tuple(sorted(kwargs.items()))
+ # return as a string
+ return str(key)
+
+
+def cache_for_request(fn):
+ """
+ Decorator that allows to cache a function call with parameters and its result only for the current request
+ The result is stored in the memory of the current process
+ As soon as the request is destroyed, the cache is destroyed
+ :param fn:
+ :return:
+ """
+ def wrapper(*args, **kwargs):
+ cache = get_request_cache()
+
+ if not cache:
+ # no cache found -> directly execute function without caching
+ return fn(*args, **kwargs)
+
+ # cache found -> check if a result is already available for this function call
+ key = cache_calculate_key(fn.__name__, *args, **kwargs)
+ result = getattr(cache, key, None)
+
+ if not result:
+ # no result available -> execute function
+ result = fn(*args, **kwargs)
+ setattr(cache, key, result)
+ else:
+ print('"{}"'.format(key), "Youpi")
+
+ return result
+ return wrapper
+
+
+def set_default_cache(cache, request):
+ has_ishtar_user = hasattr(request.user, "ishtaruser")
+ cache.set("ishtar_user.exists", has_ishtar_user)
+ if not has_ishtar_user:
+ return
+ cache.set("ishtar_user.pk", request.user.ishtaruser.pk)
+ is_admin = request.user.ishtaruser.has_right("administrator", request.session)
+ cache.set("ishtar_user.is_administrator", is_admin)
+
+ # profile
+ current_profile = request.user.ishtaruser.current_profile
+ cache.set("ishtar_user.has_profile", current_profile is not None)
+ if current_profile:
+ cache.set("ishtar_user.profile.profile_type_pk",
+ current_profile.profile_type_id)
+ cache.set("ishtar_user.profile.auto_pin",
+ current_profile.auto_pin)