diff --git a/api/buildings/models.py b/api/buildings/models.py index bf928b31..4b92e27e 100644 --- a/api/buildings/models.py +++ b/api/buildings/models.py @@ -6,7 +6,6 @@ import PIL.Image from django.conf import settings -from django.core.cache import cache from django.core.exceptions import ValidationError from django.core.files.uploadedfile import InMemoryUploadedFile from django.db import models @@ -15,8 +14,9 @@ from django.utils.safestring import mark_safe from django.utils.translation import get_language, gettext_lazy as _ +from seismic_site.caching import delete_from_cache + BUILDINGS_LISTING_CACHE_KEY = "all_buildings_listing" -BUILDINGS_LISTING_CACHE_TIMEOUT = 60 * 60 * 24 # 24 hours class SeismicCategoryChoice(Enum): @@ -103,7 +103,7 @@ def bulk_create(self, *args, **kwargs): super().bulk_create(self, *args, **kwargs) Statistic.update_statistics() - cache.delete(BUILDINGS_LISTING_CACHE_KEY) + delete_from_cache(BUILDINGS_LISTING_CACHE_KEY) class ApprovedBuilding(models.Manager): diff --git a/api/buildings/signals.py b/api/buildings/signals.py index ecddf7bb..1c4b9dc7 100644 --- a/api/buildings/signals.py +++ b/api/buildings/signals.py @@ -1,7 +1,7 @@ -from django.core.cache import cache from django.db.models.signals import post_save from django.dispatch import receiver +from seismic_site.caching import delete_from_cache from .models import BUILDINGS_LISTING_CACHE_KEY, Building, Statistic @@ -10,7 +10,7 @@ def refresh_cache(instance: Building, **kwargs): if not isinstance(instance, Building): return - cache.delete(BUILDINGS_LISTING_CACHE_KEY) + delete_from_cache(BUILDINGS_LISTING_CACHE_KEY) @receiver(post_save, sender=Building) diff --git a/api/buildings/views.py b/api/buildings/views.py index f44c0bbb..7064cf1c 100644 --- a/api/buildings/views.py +++ b/api/buildings/views.py @@ -2,16 +2,15 @@ from django.conf import settings from django.contrib.postgres.search import TrigramSimilarity -from django.core.cache import cache from drf_spectacular.utils import OpenApiParameter, extend_schema from rest_framework import permissions, status, viewsets from rest_framework.decorators import action, api_view, permission_classes from rest_framework.response import Response from rest_framework.throttling import AnonRateThrottle +from seismic_site.caching import get_from_cache, set_to_cache from .models import ( BUILDINGS_LISTING_CACHE_KEY, - BUILDINGS_LISTING_CACHE_TIMEOUT, Building, BuildingProximalUtilities, BuildingWorkPerformed, @@ -49,7 +48,7 @@ def list(self, request, *args, **kwargs): """ nonexistent = object() # sentinel - cached_data: List = cache.get(BUILDINGS_LISTING_CACHE_KEY, nonexistent) + cached_data: List = get_from_cache(BUILDINGS_LISTING_CACHE_KEY, nonexistent) if cached_data is nonexistent: queryset = self.filter_queryset(self.get_queryset()) @@ -62,7 +61,7 @@ def list(self, request, *args, **kwargs): serializer = self.get_serializer(queryset, many=True) cached_data = serializer.data - cache.set(BUILDINGS_LISTING_CACHE_KEY, cached_data, timeout=BUILDINGS_LISTING_CACHE_TIMEOUT) + set_to_cache(BUILDINGS_LISTING_CACHE_KEY, cached_data) return Response(cached_data) @@ -145,10 +144,7 @@ class WorkPerformedViewSet(viewsets.ReadOnlyModelViewSet): serializer_class = WorkPerformedSerializer -@extend_schema( - request=StatisticSerializer, - responses=None # TODO: is it really None? -) +@extend_schema(request=StatisticSerializer, responses=None) # TODO: is it really None? @api_view(["GET"]) @permission_classes((permissions.AllowAny,)) def statistics(request): diff --git a/api/seismic_site/caching.py b/api/seismic_site/caching.py new file mode 100644 index 00000000..fbe85a2b --- /dev/null +++ b/api/seismic_site/caching.py @@ -0,0 +1,24 @@ +from typing import Any + +from django.core.cache import caches + + +def get_from_cache(data_key: str, default: Any = object()) -> Any: + cached_data = caches["memory"].get(data_key, default) + + if cached_data is default: + cached_data = caches["default"].get(data_key, default) + if cached_data is not default: + caches["memory"].set(data_key, cached_data) + + return cached_data + + +def set_to_cache(data_key: str, data: Any, timeout: int = None) -> None: + for cache in caches.all(): + cache.set(data_key, data, timeout=timeout) + + +def delete_from_cache(data_key: str) -> None: + for cache in caches.all(): + cache.delete(data_key) diff --git a/api/seismic_site/settings/base.py b/api/seismic_site/settings/base.py index 6a72a25e..23d6c519 100644 --- a/api/seismic_site/settings/base.py +++ b/api/seismic_site/settings/base.py @@ -142,9 +142,15 @@ CACHES = { "default": { + "BACKEND": "django.core.cache.backends.db.DatabaseCache", + "LOCATION": "seismic_cache_table", + "TIMEOUT": 60 * 60 * 24, # 24 hours + }, + "memory": { "BACKEND": "django.core.cache.backends.locmem.LocMemCache", "LOCATION": "unique-snowflake", - } + "TIMEOUT": 60 * 5, # 5 minutes + }, } # Internationalization