added api pagination for recipes

This commit is contained in:
vabene1111 2021-04-18 14:05:19 +02:00
parent 04488741c4
commit e676b4bac3
4 changed files with 95 additions and 30 deletions

View File

@ -50,7 +50,7 @@ def search_recipes(queryset, params):
if search_internal == 'true': if search_internal == 'true':
queryset = queryset.filter(internal=True) queryset = queryset.filter(internal=True)
if search_limit: # if search_limit:
queryset = queryset[:int(search_limit)] # queryset = queryset[:int(search_limit)]
return queryset return queryset

File diff suppressed because one or more lines are too long

View File

@ -4,6 +4,7 @@ import re
import uuid import uuid
import requests import requests
import yaml
from PIL import Image from PIL import Image
from annoying.decorators import ajax_request from annoying.decorators import ajax_request
from annoying.functions import get_object_or_None from annoying.functions import get_object_or_None
@ -19,8 +20,10 @@ from django.utils.translation import gettext as _
from icalendar import Calendar, Event from icalendar import Calendar, Event
from rest_framework import decorators, viewsets, status from rest_framework import decorators, viewsets, status
from rest_framework.exceptions import APIException, PermissionDenied, NotFound, MethodNotAllowed from rest_framework.exceptions import APIException, PermissionDenied, NotFound, MethodNotAllowed
from rest_framework.pagination import LimitOffsetPagination, PageNumberPagination
from rest_framework.parsers import MultiPartParser from rest_framework.parsers import MultiPartParser
from rest_framework.response import Response from rest_framework.response import Response
from rest_framework.schemas import AutoSchema
from rest_framework.viewsets import ViewSetMixin from rest_framework.viewsets import ViewSetMixin
from cookbook.helper.ingredient_parser import parse from cookbook.helper.ingredient_parser import parse
@ -49,7 +52,8 @@ from cookbook.serializer import (FoodSerializer, IngredientSerializer,
StorageSerializer, SyncLogSerializer, StorageSerializer, SyncLogSerializer,
SyncSerializer, UnitSerializer, SyncSerializer, UnitSerializer,
UserNameSerializer, UserPreferenceSerializer, UserNameSerializer, UserPreferenceSerializer,
ViewLogSerializer, CookLogSerializer, RecipeBookEntrySerializer, RecipeOverviewSerializer, SupermarketSerializer, ImportLogSerializer) ViewLogSerializer, CookLogSerializer, RecipeBookEntrySerializer,
RecipeOverviewSerializer, SupermarketSerializer, ImportLogSerializer)
from recipes.settings import DEMO from recipes.settings import DEMO
from recipe_scrapers import scrape_me, WebsiteNotImplementedError, NoSchemaFoundInWildMode from recipe_scrapers import scrape_me, WebsiteNotImplementedError, NoSchemaFoundInWildMode
@ -248,7 +252,8 @@ class MealTypeViewSet(viewsets.ModelViewSet):
permission_classes = [CustomIsOwner] permission_classes = [CustomIsOwner]
def get_queryset(self): def get_queryset(self):
queryset = self.queryset.order_by('order', 'id').filter(created_by=self.request.user).filter(space=self.request.space).all() queryset = self.queryset.order_by('order', 'id').filter(created_by=self.request.user).filter(
space=self.request.space).all()
return queryset return queryset
@ -270,29 +275,59 @@ class StepViewSet(viewsets.ModelViewSet):
return self.queryset.filter(recipe__space=self.request.space) return self.queryset.filter(recipe__space=self.request.space)
class RecipePagination(PageNumberPagination):
page_size = 25
page_size_query_param = 'page_size'
max_page_size = 100
from rest_framework.schemas.openapi import AutoSchema, SchemaGenerator
class RecipeSchema(AutoSchema):
def get_path_parameters(self, path, method):
parameters = super().get_path_parameters(path, method)
parameters.append(
{
"name": 'query',
"in": "path",
"required": True,
"description": 'description',
'schema': {
'type': 'string', # TODO: integer, pattern, ...
},
}
)
return parameters
class RecipeViewSet(viewsets.ModelViewSet): class RecipeViewSet(viewsets.ModelViewSet):
""" """
list: list:
optional parameters optional parameters
- **query**: search recipes for a string contained in the recipe name (case in-sensitive)
- **query**: search recipes for a string contained - **limit**: limits the amount of returned results
in the recipe name (case in-sensitive) """
- **limit**: limits the amount of returned results
"""
queryset = Recipe.objects queryset = Recipe.objects
serializer_class = RecipeSerializer serializer_class = RecipeSerializer
# TODO split read and write permission for meal plan guest # TODO split read and write permission for meal plan guest
permission_classes = [CustomIsShare | CustomIsGuest] permission_classes = [CustomIsShare | CustomIsGuest]
pagination_class = RecipePagination
# schema = RecipeSchema
def get_queryset(self): def get_queryset(self):
share = self.request.query_params.get('share', None) share = self.request.query_params.get('share', None)
if not (share and self.detail): if not (share and self.detail):
self.queryset = self.queryset.filter(space=self.request.space) self.queryset = self.queryset.filter(space=self.request.space)
return search_recipes(self.queryset, self.request.GET) self.queryset = search_recipes(self.queryset, self.request.GET)
return super().get_queryset()
# TODO write extensive tests for permissions # TODO write extensive tests for permissions
def get_serializer_class(self): def get_serializer_class(self):
if self.action == 'list': if self.action == 'list':
return RecipeOverviewSerializer return RecipeOverviewSerializer
@ -341,7 +376,9 @@ class ShoppingListRecipeViewSet(viewsets.ModelViewSet):
permission_classes = [CustomIsOwner | CustomIsShared] permission_classes = [CustomIsOwner | CustomIsShared]
def get_queryset(self): def get_queryset(self):
return self.queryset.filter(Q(shoppinglist__created_by=self.request.user) | Q(shoppinglist__shared=self.request.user)).filter(shoppinglist__space=self.request.space).all() return self.queryset.filter(
Q(shoppinglist__created_by=self.request.user) | Q(shoppinglist__shared=self.request.user)).filter(
shoppinglist__space=self.request.space).all()
class ShoppingListEntryViewSet(viewsets.ModelViewSet): class ShoppingListEntryViewSet(viewsets.ModelViewSet):
@ -350,7 +387,9 @@ class ShoppingListEntryViewSet(viewsets.ModelViewSet):
permission_classes = [CustomIsOwner | CustomIsShared] permission_classes = [CustomIsOwner | CustomIsShared]
def get_queryset(self): def get_queryset(self):
return self.queryset.filter(Q(shoppinglist__created_by=self.request.user) | Q(shoppinglist__shared=self.request.user)).filter(shoppinglist__space=self.request.space).all() return self.queryset.filter(
Q(shoppinglist__created_by=self.request.user) | Q(shoppinglist__shared=self.request.user)).filter(
shoppinglist__space=self.request.space).all()
class ShoppingListViewSet(viewsets.ModelViewSet): class ShoppingListViewSet(viewsets.ModelViewSet):
@ -359,7 +398,8 @@ class ShoppingListViewSet(viewsets.ModelViewSet):
permission_classes = [CustomIsOwner | CustomIsShared] permission_classes = [CustomIsOwner | CustomIsShared]
def get_queryset(self): def get_queryset(self):
return self.queryset.filter(Q(created_by=self.request.user) | Q(shared=self.request.user)).filter(space=self.request.space).distinct() return self.queryset.filter(Q(created_by=self.request.user) | Q(shared=self.request.user)).filter(
space=self.request.space).distinct()
def get_serializer_class(self): def get_serializer_class(self):
try: try:
@ -562,7 +602,8 @@ def recipe_from_url_old(request):
url = request.POST['url'] url = request.POST['url']
headers = { headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.106 Safari/537.36' # noqa: E501 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.106 Safari/537.36'
# noqa: E501
} }
try: try:
response = requests.get(url, headers=headers) response = requests.get(url, headers=headers)

View File

@ -51,7 +51,6 @@
</div> </div>
<div class="row"> <div class="row">
<div class="col-12"> <div class="col-12">
@ -60,11 +59,12 @@
<generic-multiselect @change="genericSelectChanged" parent_variable="search_keywords" <generic-multiselect @change="genericSelectChanged" parent_variable="search_keywords"
:initial_selection="search_keywords" :initial_selection="search_keywords"
search_function="listKeywords" label="label" style="flex-grow: 1; flex-shrink: 1; flex-basis: 0" search_function="listKeywords" label="label"
style="flex-grow: 1; flex-shrink: 1; flex-basis: 0"
v-bind:placeholder="$t('Keywords')"></generic-multiselect> v-bind:placeholder="$t('Keywords')"></generic-multiselect>
<b-input-group-append > <b-input-group-append>
<b-input-group-text > <b-input-group-text>
<b-form-checkbox v-model="search_keywords_or" name="check-button" @change="refreshData" <b-form-checkbox v-model="search_keywords_or" name="check-button" @change="refreshData"
class="shadow-none" switch> class="shadow-none" switch>
@ -83,10 +83,11 @@
<b-input-group style="margin-top: 1vh"> <b-input-group style="margin-top: 1vh">
<generic-multiselect @change="genericSelectChanged" parent_variable="search_foods" <generic-multiselect @change="genericSelectChanged" parent_variable="search_foods"
:initial_selection="search_foods" :initial_selection="search_foods"
search_function="listFoods" label="name" style="flex-grow: 1; flex-shrink: 1; flex-basis: 0" search_function="listFoods" label="name"
style="flex-grow: 1; flex-shrink: 1; flex-basis: 0"
v-bind:placeholder="$t('Ingredients')"></generic-multiselect> v-bind:placeholder="$t('Ingredients')"></generic-multiselect>
<b-input-group-append > <b-input-group-append>
<b-input-group-text > <b-input-group-text>
<b-form-checkbox v-model="search_foods_or" name="check-button" @change="refreshData" <b-form-checkbox v-model="search_foods_or" name="check-button" @change="refreshData"
class="shadow-none" switch> class="shadow-none" switch>
<span class="text-uppercase" v-if="search_foods_or">{{ $t('or') }}</span> <span class="text-uppercase" v-if="search_foods_or">{{ $t('or') }}</span>
@ -105,10 +106,11 @@
<b-input-group style="margin-top: 1vh"> <b-input-group style="margin-top: 1vh">
<generic-multiselect @change="genericSelectChanged" parent_variable="search_books" <generic-multiselect @change="genericSelectChanged" parent_variable="search_books"
:initial_selection="search_books" :initial_selection="search_books"
search_function="listRecipeBooks" label="name" style="flex-grow: 1; flex-shrink: 1; flex-basis: 0" search_function="listRecipeBooks" label="name"
style="flex-grow: 1; flex-shrink: 1; flex-basis: 0"
v-bind:placeholder="$t('Books')"></generic-multiselect> v-bind:placeholder="$t('Books')"></generic-multiselect>
<b-input-group-append > <b-input-group-append>
<b-input-group-text > <b-input-group-text>
<b-form-checkbox v-model="search_books_or" name="check-button" @change="refreshData" <b-form-checkbox v-model="search_books_or" name="check-button" @change="refreshData"
class="shadow-none" tyle="width: 100%" switch> class="shadow-none" tyle="width: 100%" switch>
@ -139,6 +141,21 @@
</div> </div>
</div> </div>
</div> </div>
<div class="row" style="margin-top: 2vh">
<div class="col col-md-12">
<b-pagination
v-model="pagination_page"
:total-rows="pagination_count"
per-page="25"
@change="pageChange"
align="center">
</b-pagination>
</div>
</div>
</div> </div>
<div class="col-md-2 d-none d-md-block"> <div class="col-md-2 d-none d-md-block">
@ -185,6 +202,8 @@ export default {
advanced_search_visible: true, advanced_search_visible: true,
pagination_count: 0,
pagination_page: 1,
} }
}, },
@ -213,10 +232,11 @@ export default {
books_or: this.search_books_or, books_or: this.search_books_or,
internal: this.search_internal, internal: this.search_internal,
limit: 20, page: this.pagination_page,
} }
}).then(result => { }).then(result => {
this.recipes = result.data this.recipes = result.data.results
this.pagination_count = result.data.count
}) })
}, },
genericSelectChanged: function (obj) { genericSelectChanged: function (obj) {
@ -230,6 +250,10 @@ export default {
this.search_foods = [] this.search_foods = []
this.search_books = [] this.search_books = []
this.refreshData() this.refreshData()
},
pageChange: function (page){
this.pagination_page = page
this.refreshData()
} }
} }
} }