From 8edea0b92fdfa014a711c1f142daa66297fc1412 Mon Sep 17 00:00:00 2001 From: Daniel Egger Date: Tue, 5 Jul 2022 15:44:11 +0200 Subject: [PATCH] Add page cache for learningpath pages --- client/src/stores/learningPath.ts | 2 +- scripts/count_queries.py | 7 +++--- server/config/settings/base.py | 23 ++++++++----------- server/vbv_lernwelt/learnpath/models.py | 6 +++++ .../learnpath/serializer_helpers.py | 9 ++++---- server/vbv_lernwelt/learnpath/serializers.py | 21 ----------------- server/vbv_lernwelt/learnpath/signals.py | 16 +++++++++++++ server/vbv_lernwelt/learnpath/urls.py | 6 ++--- server/vbv_lernwelt/learnpath/views.py | 17 +++++--------- 9 files changed, 48 insertions(+), 59 deletions(-) create mode 100644 server/vbv_lernwelt/learnpath/signals.py diff --git a/client/src/stores/learningPath.ts b/client/src/stores/learningPath.ts index 58e231c6..8422a49d 100644 --- a/client/src/stores/learningPath.ts +++ b/client/src/stores/learningPath.ts @@ -25,7 +25,7 @@ export const useLearningPathStore = defineStore({ return this.learningPath; } try { - const learningPathData = await itGet(`/learnpath/api/learningpath/${slug}/`); + const learningPathData = await itGet(`/learnpath/api/page/${slug}/`); const completionData = await itGet(`/api/completion/learning_path/${learningPathData.translation_key}/`); this.learningPath = learningPathData; diff --git a/scripts/count_queries.py b/scripts/count_queries.py index dc65881c..48167c3b 100644 --- a/scripts/count_queries.py +++ b/scripts/count_queries.py @@ -11,8 +11,7 @@ os.environ.setdefault("IT_APP_ENVIRONMENT", "development") os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings.base") django.setup() -from vbv_lernwelt.learnpath.models import LearningPath -from vbv_lernwelt.learnpath.serializers import LearningPathSerializer +from wagtail.models import Page def main(): @@ -22,9 +21,9 @@ def main(): from django.db import reset_queries reset_queries() - learning_path = LearningPath.objects.filter(slug='versicherungsvermittlerin', locale__language_code='de-CH').first() + page = Page.objects.get(slug='versicherungsvermittlerin', locale__language_code='de-CH') + serializer = page.specific.get_serializer_class()(page.specific) - serializer = LearningPathSerializer(learning_path) print(serializer.data) print(len(json.dumps(serializer.data))) print(len(connection.queries)) diff --git a/server/config/settings/base.py b/server/config/settings/base.py index 176f7ba0..dcd70113 100644 --- a/server/config/settings/base.py +++ b/server/config/settings/base.py @@ -486,31 +486,28 @@ ALLOWED_HOSTS = env.list( # CACHES CACHES = { "default": { - "BACKEND": env( - "IT_DJANGO_CACHE_BACKEND", - default="django.core.cache.backends.db.DatabaseCache", - ), + "BACKEND": env("IT_DJANGO_CACHE_BACKEND", default="django.core.cache.backends.db.DatabaseCache"), "LOCATION": env("IT_DJANGO_CACHE_LOCATION", default="django_cache_table"), - } + }, } if "django_redis.cache.RedisCache" in env("IT_DJANGO_CACHE_BACKEND", default=""): CACHES = { "default": { - "BACKEND": env( - "IT_DJANGO_CACHE_BACKEND", - default="django.core.cache.backends.db.DatabaseCache", - ), - "LOCATION": env("IT_DJANGO_CACHE_LOCATION", default="django_cache_table"), + "BACKEND": "django_redis.cache.RedisCache", + "LOCATION": env("IT_DJANGO_CACHE_LOCATION"), "OPTIONS": { "CLIENT_CLASS": "django_redis.client.DefaultClient", - # Mimicing memcache behavior. - # https://github.com/jazzband/django-redis#memcached-exceptions-behavior "IGNORE_EXCEPTIONS": True, }, - } + }, } +CACHES["learning_path_cache"] = { + "BACKEND": "django.core.cache.backends.db.DatabaseCache", + "LOCATION": "django_cache_learning_path", +} + # OAuth/OpenId Connect OAUTH = { diff --git a/server/vbv_lernwelt/learnpath/models.py b/server/vbv_lernwelt/learnpath/models.py index 0e3d1c57..f2d8344b 100644 --- a/server/vbv_lernwelt/learnpath/models.py +++ b/server/vbv_lernwelt/learnpath/models.py @@ -29,6 +29,12 @@ class LearningPath(Page): def __str__(self): return f"{self.title}" + @classmethod + def get_serializer_class(cls): + return get_it_serializer_class( + cls, ['id', 'title', 'slug', 'type', 'translation_key', 'children'] + ) + class Topic(Page): # title = models.TextField(default='') diff --git a/server/vbv_lernwelt/learnpath/serializer_helpers.py b/server/vbv_lernwelt/learnpath/serializer_helpers.py index 5ebfda78..73da6ade 100644 --- a/server/vbv_lernwelt/learnpath/serializer_helpers.py +++ b/server/vbv_lernwelt/learnpath/serializer_helpers.py @@ -25,11 +25,10 @@ class ItBaseSerializer(wagtail_serializers.BaseSerializer): super().__init__(*args, **kwargs) def get_children(self, obj): - if self.descendants: - children = _get_children(self.descendants, obj) - return [c.specific.get_serializer_class()(c.specific, descendants=self.descendants).data for c in children] - else: - return [c.specific.get_serializer_class()(c.specific).data for c in obj.get_children().specific()] + if not self.descendants: + self.descendants = [p for p in obj.get_descendants().specific()] + children = _get_children(self.descendants, obj) + return [c.specific.get_serializer_class()(c.specific, descendants=self.descendants).data for c in children] def _get_descendants(pages, obj): diff --git a/server/vbv_lernwelt/learnpath/serializers.py b/server/vbv_lernwelt/learnpath/serializers.py index ae4dadd9..e69de29b 100644 --- a/server/vbv_lernwelt/learnpath/serializers.py +++ b/server/vbv_lernwelt/learnpath/serializers.py @@ -1,21 +0,0 @@ -from rest_framework import serializers - -from vbv_lernwelt.learnpath.models import Circle, LearningPath -from vbv_lernwelt.learnpath.serializer_helpers import get_it_serializer_class, _get_children - - -class LearningPathSerializer(get_it_serializer_class(LearningPath, [])): - children = serializers.SerializerMethodField() - - meta_fields = [] - - def get_children(self, obj): - descendants = [p for p in obj.get_descendants().specific()] - return [c.specific.get_serializer_class()(c.specific, descendants=descendants).data for c in _get_children(descendants, obj)] - - def get_meta_label(self, obj): - return obj._meta.label - - class Meta: - model = Circle - fields = ['id', 'title', 'slug', 'type', 'translation_key', 'children'] diff --git a/server/vbv_lernwelt/learnpath/signals.py b/server/vbv_lernwelt/learnpath/signals.py new file mode 100644 index 00000000..48369730 --- /dev/null +++ b/server/vbv_lernwelt/learnpath/signals.py @@ -0,0 +1,16 @@ +import structlog +from django.core.cache import caches +from django.db.models.signals import post_delete, post_save +from wagtail.models import Page + +logger = structlog.get_logger(__name__) + + +def invalidate_learning_path_cache(sender, **kwargs): + logger.debug('invalidate learning_path_cache', label='learning_path_cache') + caches['learning_path_cache'].clear() + + +for subclass in Page.__subclasses__(): + post_save.connect(invalidate_learning_path_cache, subclass) + post_delete.connect(invalidate_learning_path_cache, subclass) diff --git a/server/vbv_lernwelt/learnpath/urls.py b/server/vbv_lernwelt/learnpath/urls.py index 5b470619..2d12205e 100644 --- a/server/vbv_lernwelt/learnpath/urls.py +++ b/server/vbv_lernwelt/learnpath/urls.py @@ -1,10 +1,8 @@ from django.urls import path, re_path -from .views import circle_view, generate_web_component_icons -from .views import learningpath_view +from .views import generate_web_component_icons, page_api_view urlpatterns = [ - path(r"api/circle//", circle_view, name="circle_view"), - path(r"api/learningpath//", learningpath_view, name="learningpath_view"), + path(r"api/page//", page_api_view, name="page_api_view"), re_path(r"icons/$", generate_web_component_icons, name="generate_web_component_icons"), ] diff --git a/server/vbv_lernwelt/learnpath/views.py b/server/vbv_lernwelt/learnpath/views.py index 962bc23c..c1ea8f26 100644 --- a/server/vbv_lernwelt/learnpath/views.py +++ b/server/vbv_lernwelt/learnpath/views.py @@ -4,18 +4,19 @@ from pathlib import Path from django.conf import settings from django.shortcuts import render +from django.views.decorators.cache import cache_page from rest_framework.decorators import api_view from rest_framework.response import Response +from wagtail.models import Page from vbv_lernwelt.core.middleware.auth import django_view_authentication_exempt -from vbv_lernwelt.learnpath.models import Circle, LearningPath -from vbv_lernwelt.learnpath.serializers import LearningPathSerializer @api_view(['GET']) -def circle_view(request, slug): - circle = Circle.objects.get(slug=slug) - serializer = Circle.get_serializer_class()(circle) +@cache_page(60 * 60 * 8, cache="learning_path_cache") +def page_api_view(request, slug): + page = Page.objects.get(slug=slug, locale__language_code='de-CH') + serializer = page.specific.get_serializer_class()(page.specific) return Response(serializer.data) @@ -38,9 +39,3 @@ def generate_web_component_icons(request): context={'svg_files': svg_files}, content_type="application/javascript" ) - -@api_view(['GET']) -def learningpath_view(request, slug): - learning_path = LearningPath.objects.get(slug=slug, locale__language_code='de-CH') - serializer = LearningPathSerializer(learning_path) - return Response(serializer.data)