mirror of
https://github.com/mealie-recipes/mealie.git
synced 2026-02-14 20:03:14 -05:00
feat: re-write get all routes to use pagination (#1424)
rewrite get_all routes to use a pagination pattern to allow for better implementations of search, filter, and sorting on the frontend or by any client without fetching all the data. Additionally we added a CI check for running the Nuxt built to confirm that no TS errors were present. Finally, I had to remove the header support for the Shopping lists as the browser caching based off last_updated header was not allowing it to read recent updates due to how we're handling the updated_at property in the database with nested fields. This will have to be looked at in the future to reimplement. I'm unsure how many other routes have a similar issue. Co-authored-by: Hayden <64056131+hay-kot@users.noreply.github.com>
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
from math import ceil
|
||||
from random import randint
|
||||
from typing import Any, Optional
|
||||
from uuid import UUID
|
||||
@@ -7,6 +8,7 @@ from slugify import slugify
|
||||
from sqlalchemy import and_, func
|
||||
from sqlalchemy.exc import IntegrityError
|
||||
from sqlalchemy.orm import joinedload
|
||||
from sqlalchemy.sql import sqltypes
|
||||
|
||||
from mealie.db.models.recipe.category import Category
|
||||
from mealie.db.models.recipe.ingredient import RecipeIngredient
|
||||
@@ -15,8 +17,9 @@ from mealie.db.models.recipe.settings import RecipeSettings
|
||||
from mealie.db.models.recipe.tag import Tag
|
||||
from mealie.db.models.recipe.tool import Tool
|
||||
from mealie.schema.recipe import Recipe
|
||||
from mealie.schema.recipe.recipe import RecipeCategory, RecipeSummary, RecipeTag, RecipeTool
|
||||
from mealie.schema.recipe.recipe import RecipeCategory, RecipePagination, RecipeSummary, RecipeTag, RecipeTool
|
||||
from mealie.schema.recipe.recipe_category import CategoryBase, TagBase
|
||||
from mealie.schema.response.pagination import OrderDirection, PaginationQuery
|
||||
|
||||
from .repository_generic import RepositoryGeneric
|
||||
|
||||
@@ -128,6 +131,72 @@ class RepositoryRecipes(RepositoryGeneric[Recipe, RecipeModel]):
|
||||
.all()
|
||||
)
|
||||
|
||||
def page_all(self, pagination: PaginationQuery, override=None, load_food=False) -> RecipePagination:
|
||||
q = self.session.query(self.model)
|
||||
|
||||
args = [
|
||||
joinedload(RecipeModel.recipe_category),
|
||||
joinedload(RecipeModel.tags),
|
||||
joinedload(RecipeModel.tools),
|
||||
]
|
||||
|
||||
if load_food:
|
||||
args.append(joinedload(RecipeModel.recipe_ingredient).options(joinedload(RecipeIngredient.food)))
|
||||
|
||||
q = q.options(*args)
|
||||
|
||||
fltr = self._filter_builder()
|
||||
q = q.filter_by(**fltr)
|
||||
count = q.count()
|
||||
|
||||
# interpret -1 as "get_all"
|
||||
if pagination.per_page == -1:
|
||||
pagination.per_page = count
|
||||
|
||||
try:
|
||||
total_pages = ceil(count / pagination.per_page)
|
||||
|
||||
except ZeroDivisionError:
|
||||
total_pages = 0
|
||||
|
||||
# interpret -1 as "last page"
|
||||
if pagination.page == -1:
|
||||
pagination.page = total_pages
|
||||
|
||||
# failsafe for user input error
|
||||
if pagination.page < 1:
|
||||
pagination.page = 1
|
||||
|
||||
if pagination.order_by:
|
||||
if order_attr := getattr(self.model, pagination.order_by, None):
|
||||
# queries handle uppercase and lowercase differently, which is undesirable
|
||||
if isinstance(order_attr.type, sqltypes.String):
|
||||
order_attr = func.lower(order_attr)
|
||||
|
||||
if pagination.order_direction == OrderDirection.asc:
|
||||
order_attr = order_attr.asc()
|
||||
elif pagination.order_direction == OrderDirection.desc:
|
||||
order_attr = order_attr.desc()
|
||||
|
||||
q = q.order_by(order_attr)
|
||||
|
||||
q = q.limit(pagination.per_page).offset((pagination.page - 1) * pagination.per_page)
|
||||
|
||||
try:
|
||||
data = q.all()
|
||||
except Exception as e:
|
||||
self._log_exception(e)
|
||||
self.session.rollback()
|
||||
raise e
|
||||
|
||||
return RecipePagination(
|
||||
page=pagination.page,
|
||||
per_page=pagination.per_page,
|
||||
total=count,
|
||||
total_pages=total_pages,
|
||||
items=data,
|
||||
)
|
||||
|
||||
def get_by_categories(self, categories: list[RecipeCategory]) -> list[RecipeSummary]:
|
||||
"""
|
||||
get_by_categories returns all the Recipes that contain every category provided in the list
|
||||
|
||||
Reference in New Issue
Block a user