From fcf861f5eb30ba91fc96f6d0861b02ac3db8e8dd Mon Sep 17 00:00:00 2001 From: vabene1111 Date: Sun, 26 Feb 2023 08:49:44 +0100 Subject: [PATCH] cleaner caching function --- cookbook/serializer.py | 41 +++++++++++++++++++++++++---------------- cookbook/views/api.py | 17 +++++++++++++++-- 2 files changed, 40 insertions(+), 18 deletions(-) diff --git a/cookbook/serializer.py b/cookbook/serializer.py index 7a4adda36..19dc89ba0 100644 --- a/cookbook/serializer.py +++ b/cookbook/serializer.py @@ -7,6 +7,7 @@ from html import escape from smtplib import SMTPException from django.contrib.auth.models import Group, User, AnonymousUser +from django.core.cache import caches from django.core.mail import send_mail from django.db.models import Avg, Q, QuerySet, Sum from django.http import BadHeaderError @@ -103,15 +104,17 @@ class CustomOnHandField(serializers.Field): return instance def to_representation(self, obj): - shared_users = None - if request := self.context.get('request', None): - shared_users = getattr(request, '_shared_users', None) - if shared_users is None: + shared_users = [] + if c := caches['default'].get(f'shopping_shared_users_{self.context["request"].space.id}_{self.context["request"].user.id}', None): + shared_users = c + else: try: shared_users = [x.id for x in list(self.context['request'].user.get_shopping_share())] + [ self.context['request'].user.id] + caches['default'].set(f'shopping_shared_users_{self.context["request"].space.id}_{self.context["request"].user.id}', shared_users, timeout=5*60) + # TODO ugly hack that improves API performance significantly, should be done properly except AttributeError: # Anonymous users (using share links) don't have shared users - shared_users = [] + pass return obj.onhand_users.filter(id__in=shared_users).exists() def to_internal_value(self, data): @@ -278,11 +281,12 @@ class SpaceSerializer(WritableNestedModelSerializer): class Meta: model = Space fields = ( - 'id', 'name', 'created_by', 'created_at', 'message', 'max_recipes', 'max_file_storage_mb', 'max_users', - 'allow_sharing', 'demo', 'food_inherit', 'show_facet_count', 'user_count', 'recipe_count', 'file_size_mb', - 'image', 'use_plural',) + 'id', 'name', 'created_by', 'created_at', 'message', 'max_recipes', 'max_file_storage_mb', 'max_users', + 'allow_sharing', 'demo', 'food_inherit', 'show_facet_count', 'user_count', 'recipe_count', 'file_size_mb', + 'image', 'use_plural',) read_only_fields = ( - 'id', 'created_by', 'created_at', 'max_recipes', 'max_file_storage_mb', 'max_users', 'allow_sharing', 'demo',) + 'id', 'created_by', 'created_at', 'max_recipes', 'max_file_storage_mb', 'max_users', 'allow_sharing', + 'demo',) class UserSpaceSerializer(WritableNestedModelSerializer): @@ -531,15 +535,20 @@ class FoodSerializer(UniqueFieldsMixin, WritableNestedModelSerializer, ExtendedR images = ['recipe__image'] def get_substitute_onhand(self, obj): - shared_users = None - if request := self.context.get('request', None): - shared_users = getattr(request, '_shared_users', None) - if shared_users is None: + shared_users = [] + if c := caches['default'].get( + f'shopping_shared_users_{self.context["request"].space.id}_{self.context["request"].user.id}', None): + shared_users = c + else: try: shared_users = [x.id for x in list(self.context['request'].user.get_shopping_share())] + [ self.context['request'].user.id] - except AttributeError: - shared_users = [] + caches['default'].set( + f'shopping_shared_users_{self.context["request"].space.id}_{self.context["request"].user.id}', + shared_users, timeout=5 * 60) + # TODO ugly hack that improves API performance significantly, should be done properly + except AttributeError: # Anonymous users (using share links) don't have shared users + pass filter = Q(id__in=obj.substitute.all()) if obj.substitute_siblings: filter |= Q(path__startswith=obj.path[:Food.steplen * (obj.depth - 1)], depth=obj.depth) @@ -630,7 +639,7 @@ class IngredientSimpleSerializer(WritableNestedModelSerializer): used_in = [] for s in obj.step_set.all(): for r in s.recipe_set.all(): - used_in.append({'id':r.id,'name':r.name}) + used_in.append({'id': r.id, 'name': r.name}) return used_in def get_conversions(self, obj): diff --git a/cookbook/views/api.py b/cookbook/views/api.py index de9e439fd..eff358956 100644 --- a/cookbook/views/api.py +++ b/cookbook/views/api.py @@ -19,6 +19,7 @@ from annoying.functions import get_object_or_None from django.contrib import messages from django.contrib.auth.models import Group, User from django.contrib.postgres.search import TrigramSimilarity +from django.core.cache import caches from django.core.exceptions import FieldError, ValidationError from django.core.files import File from django.db.models import Case, Count, Exists, OuterRef, ProtectedError, Q, Subquery, Value, When, Avg, Max @@ -526,8 +527,20 @@ class FoodViewSet(viewsets.ModelViewSet, TreeMixin): pagination_class = DefaultPagination def get_queryset(self): - self.request._shared_users = [x.id for x in list(self.request.user.get_shopping_share())] + [ - self.request.user.id] + shared_users = [] + if c := caches['default'].get( + f'shopping_shared_users_{self.request.space.id}_{self.request.user.id}', None): + shared_users = c + else: + try: + shared_users = [x.id for x in list(self.request.user.get_shopping_share())] + [ + self.request.user.id] + caches['default'].set( + f'shopping_shared_users_{self.request.space.id}_{self.request.user.id}', + shared_users, timeout=5 * 60) + # TODO ugly hack that improves API performance significantly, should be done properly + except AttributeError: # Anonymous users (using share links) don't have shared users + pass self.queryset = super().get_queryset() shopping_status = ShoppingListEntry.objects.filter(space=self.request.space, food=OuterRef('id'),