remove custom QueryParams with extend_schema decorator

This commit is contained in:
smilerz
2024-03-27 08:18:50 -05:00
parent d6929e5cf9
commit fb3473459d
2 changed files with 37 additions and 106 deletions

View File

@@ -1,72 +0,0 @@
from rest_framework.schemas.openapi import AutoSchema
from rest_framework.schemas.utils import is_list_view
class QueryParam(object):
def __init__(self, name, description=None, qtype='string', required=False):
self.name = name
self.description = description
self.qtype = qtype
self.required = required
def __str__(self):
return f'{self.name}, {self.qtype}, {self.description}'
class QueryParamAutoSchema(AutoSchema):
def is_query(self, path, method):
return is_list_view(path, method, self.view)
def get_path_parameters(self, path, method):
if not self.is_query(path, method):
return super().get_path_parameters(path, method)
parameters = super().get_path_parameters(path, method)
for q in self.view.query_params:
parameters.append({
"name": q.name, "in": "query", "required": q.required,
"description": q.description,
'schema': {'type': q.qtype, },
})
return parameters
class TreeSchema(AutoSchema):
def get_path_parameters(self, path, method):
if not is_list_view(path, method, self.view):
return super(TreeSchema, self).get_path_parameters(path, method)
api_name = path.split('/')[2]
parameters = super().get_path_parameters(path, method)
parameters.append({
"name": 'query', "in": "query", "required": False,
"description": 'Query string matched against {} name.'.format(api_name),
'schema': {'type': 'string', },
})
parameters.append({
"name": 'root', "in": "query", "required": False,
"description": 'Return first level children of {obj} with ID [int]. Integer 0 will return root {obj}s.'.format(
obj=api_name),
'schema': {'type': 'integer', },
})
parameters.append({
"name": 'tree', "in": "query", "required": False,
"description": 'Return all self and children of {} with ID [int].'.format(api_name),
'schema': {'type': 'integer', },
})
return parameters
class FilterSchema(AutoSchema):
def get_path_parameters(self, path, method):
if not is_list_view(path, method, self.view):
return super(FilterSchema, self).get_path_parameters(path, method)
api_name = path.split('/')[2]
parameters = super().get_path_parameters(path, method)
parameters.append({
"name": 'query', "in": "query", "required": False,
"description": 'Query string matched against {} name.'.format(api_name),
'schema': {'type': 'string', },
})
return parameters

View File

@@ -77,7 +77,6 @@ from cookbook.models import (Automation, BookmarkletImport, CookLog, CustomFilte
from cookbook.provider.dropbox import Dropbox from cookbook.provider.dropbox import Dropbox
from cookbook.provider.local import Local from cookbook.provider.local import Local
from cookbook.provider.nextcloud import Nextcloud from cookbook.provider.nextcloud import Nextcloud
from cookbook.schemas import FilterSchema, QueryParam, QueryParamAutoSchema, TreeSchema
from cookbook.serializer import (AccessTokenSerializer, AutomationSerializer, from cookbook.serializer import (AccessTokenSerializer, AutomationSerializer,
AutoMealPlanSerializer, BookmarkletImportListSerializer, AutoMealPlanSerializer, BookmarkletImportListSerializer,
BookmarkletImportSerializer, CookLogSerializer, BookmarkletImportSerializer, CookLogSerializer,
@@ -617,25 +616,26 @@ class FoodViewSet(viewsets.ModelViewSet, TreeMixin):
return self.serializer_class return self.serializer_class
# TODO I could not find any usage of this and it causes schema generation issues, so commenting it for now # TODO I could not find any usage of this and it causes schema generation issues, so commenting it for now
# @decorators.action(detail=True, methods=['PUT'], serializer_class=FoodShoppingUpdateSerializer, ) # this is used on the Shopping Badge
@decorators.action(detail=True, methods=['PUT'], serializer_class=FoodShoppingUpdateSerializer, )
# # TODO DRF only allows one action in a decorator action without overriding get_operation_id_base() this should be PUT and DELETE probably # # TODO DRF only allows one action in a decorator action without overriding get_operation_id_base() this should be PUT and DELETE probably
# def shopping(self, request, pk): def shopping(self, request, pk):
# if self.request.space.demo: if self.request.space.demo:
# raise PermissionDenied(detail='Not available in demo', code=None) raise PermissionDenied(detail='Not available in demo', code=None)
# obj = self.get_object() obj = self.get_object()
# shared_users = list(self.request.user.get_shopping_share()) shared_users = list(self.request.user.get_shopping_share())
# shared_users.append(request.user) shared_users.append(request.user)
# if request.data.get('_delete', False) == 'true': if request.data.get('_delete', False) == 'true':
# ShoppingListEntry.objects.filter(food=obj, checked=False, space=request.space, created_by__in=shared_users).delete() ShoppingListEntry.objects.filter(food=obj, checked=False, space=request.space, created_by__in=shared_users).delete()
# content = {'msg': _(f'{obj.name} was removed from the shopping list.')} content = {'msg': _(f'{obj.name} was removed from the shopping list.')}
# return Response(content, status=status.HTTP_204_NO_CONTENT) return Response(content, status=status.HTTP_204_NO_CONTENT)
#
# amount = request.data.get('amount', 1) amount = request.data.get('amount', 1)
# unit = request.data.get('unit', None) unit = request.data.get('unit', None)
# content = {'msg': _(f'{obj.name} was added to the shopping list.')} content = {'msg': _(f'{obj.name} was added to the shopping list.')}
#
# ShoppingListEntry.objects.create(food=obj, amount=amount, unit=unit, space=request.space, created_by=request.user) ShoppingListEntry.objects.create(food=obj, amount=amount, unit=unit, space=request.space, created_by=request.user)
# return Response(content, status=status.HTTP_204_NO_CONTENT) return Response(content, status=status.HTTP_204_NO_CONTENT)
@decorators.action(detail=True, methods=['POST'], ) @decorators.action(detail=True, methods=['POST'], )
def fdc(self, request, pk): def fdc(self, request, pk):
@@ -663,13 +663,11 @@ class FoodViewSet(viewsets.ModelViewSet, TreeMixin):
json_dumps_params={'indent': 4}) json_dumps_params={'indent': 4})
food.properties_food_amount = 100 food.properties_food_amount = 100
food.properties_food_unit = Unit.objects.get_or_create(base_unit__iexact='g', food.properties_food_unit = Unit.objects.get_or_create(
space=self.request.space, base_unit__iexact='g',
defaults={ space=self.request.space,
'name': 'g', defaults={ 'name': 'g', 'base_unit': 'g', 'space': self.request.space}
'base_unit': 'g', )[0]
'space': self.request.space
})[0]
food.save() food.save()
@@ -772,9 +770,10 @@ MealPlanViewQueryParameters = [
OpenApiParameter(name='meal_type', description=_('Filter meal plans with MealType ID. For multiple repeat parameter.'), type=str), OpenApiParameter(name='meal_type', description=_('Filter meal plans with MealType ID. For multiple repeat parameter.'), type=str),
] ]
@extend_schema_view( @extend_schema_view(
list=extend_schema( list=extend_schema(
parameters= MealPlanViewQueryParameters parameters=MealPlanViewQueryParameters
), ),
ical=extend_schema( ical=extend_schema(
parameters=MealPlanViewQueryParameters, parameters=MealPlanViewQueryParameters,
@@ -811,7 +810,7 @@ class MealPlanViewSet(viewsets.ModelViewSet):
queryset = queryset.filter(meal_type__in=meal_type) queryset = queryset.filter(meal_type__in=meal_type)
return queryset return queryset
@action(detail=False) @action(detail=False)
def ical(self, request): def ical(self, request):
from_date = self.request.query_params.get('from_date', None) from_date = self.request.query_params.get('from_date', None)
@@ -819,7 +818,6 @@ class MealPlanViewSet(viewsets.ModelViewSet):
return meal_plans_to_ical(self.get_queryset(), f'meal_plan_{from_date}-{to_date}.ics') return meal_plans_to_ical(self.get_queryset(), f'meal_plan_{from_date}-{to_date}.ics')
class AutoPlanViewSet(viewsets.ViewSet): class AutoPlanViewSet(viewsets.ViewSet):
def create(self, request): def create(self, request):
@@ -1136,8 +1134,8 @@ class RecipeViewSet(viewsets.ModelViewSet):
serializer_class=RecipeFlatSerializer, serializer_class=RecipeFlatSerializer,
) )
def flat(self, request): def flat(self, request):
qs = Recipe.objects.filter( # TODO limit fields retrieved but .values() kills image
space=request.space).filter(Q(private=False) | (Q(private=True) & (Q(created_by=self.request.user) | Q(shared=self.request.user)))).all() # TODO limit fields retrieved but .values() kills image qs = Recipe.objects.filter(space=request.space).filter(Q(private=False) | (Q(private=True) & (Q(created_by=self.request.user) | Q(shared=self.request.user)))).all()
return Response(self.serializer_class(qs, many=True).data) return Response(self.serializer_class(qs, many=True).data)
@@ -1284,14 +1282,18 @@ class ViewLogViewSet(viewsets.ModelViewSet):
return self.queryset.filter(created_by=self.request.user).filter(space=self.request.space) return self.queryset.filter(created_by=self.request.user).filter(space=self.request.space)
@extend_schema_view(
list=extend_schema(
parameters=[
OpenApiParameter(name='recipe', description='Filter for entries with the given recipe', type=int),
]
)
)
class CookLogViewSet(viewsets.ModelViewSet): class CookLogViewSet(viewsets.ModelViewSet):
queryset = CookLog.objects queryset = CookLog.objects
serializer_class = CookLogSerializer serializer_class = CookLogSerializer
permission_classes = [CustomIsOwner & CustomTokenHasReadWriteScope] permission_classes = [CustomIsOwner & CustomTokenHasReadWriteScope]
pagination_class = DefaultPagination pagination_class = DefaultPagination
query_params = [
QueryParam(name='recipe', description=_('Filter for entries with the given recipe'), qtype='integer'),
]
def get_queryset(self): def get_queryset(self):
if self.request.query_params.get('recipe', None): if self.request.query_params.get('recipe', None):
@@ -1802,6 +1804,7 @@ def get_plan_ical(request, from_date=datetime.date.today(), to_date=None):
return meal_plans_to_ical(queryset, f'meal_plan_{from_date}-{to_date}.ics') return meal_plans_to_ical(queryset, f'meal_plan_{from_date}-{to_date}.ics')
def meal_plans_to_ical(queryset, filename): def meal_plans_to_ical(queryset, filename):
cal = Calendar() cal = Calendar()