From 49bd420c10f6f17003abb978cb4725fc797fdb96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6ren?= Date: Sun, 29 Jan 2023 01:50:26 +0100 Subject: [PATCH] fix: all recipes performance regressions (#2062) * Move recipe validations from RecipeSummary to Recipe * fix RepositoryRecipes loading recipes with ingredients even when load_food is False * Add eager loading of ingredient units * fix trying to instantiate PaginationBase with concrete type not being valid for mypy * fix linting issue --- mealie/repos/repository_recipes.py | 21 ++++++--- mealie/routes/recipe/recipe_crud_routes.py | 12 ++++- mealie/schema/recipe/recipe.py | 51 ++++++++++++---------- 3 files changed, 53 insertions(+), 31 deletions(-) diff --git a/mealie/repos/repository_recipes.py b/mealie/repos/repository_recipes.py index 0f780ee7d385..5776284265de 100644 --- a/mealie/repos/repository_recipes.py +++ b/mealie/repos/repository_recipes.py @@ -16,9 +16,15 @@ from mealie.db.models.recipe.tag import Tag from mealie.db.models.recipe.tool import Tool from mealie.schema.cookbook.cookbook import ReadCookBook from mealie.schema.recipe import Recipe -from mealie.schema.recipe.recipe import RecipeCategory, RecipePagination, RecipeSummary, RecipeTag, RecipeTool +from mealie.schema.recipe.recipe import ( + RecipeCategory, + RecipeSummary, + RecipeSummaryWithIngredients, + RecipeTag, + RecipeTool, +) from mealie.schema.recipe.recipe_category import CategoryBase, TagBase -from mealie.schema.response.pagination import PaginationQuery +from mealie.schema.response.pagination import PaginationBase, PaginationQuery from .repository_generic import RepositoryGeneric @@ -139,7 +145,7 @@ class RepositoryRecipes(RepositoryGeneric[Recipe, RecipeModel]): categories: list[UUID4 | str] | None = None, tags: list[UUID4 | str] | None = None, tools: list[UUID4 | str] | None = None, - ) -> RecipePagination: + ) -> PaginationBase[RecipeSummary]: q = self.session.query(self.model) args = [ @@ -150,6 +156,10 @@ class RepositoryRecipes(RepositoryGeneric[Recipe, RecipeModel]): if load_food: args.append(joinedload(RecipeModel.recipe_ingredient).options(joinedload(RecipeIngredient.food))) + args.append(joinedload(RecipeModel.recipe_ingredient).options(joinedload(RecipeIngredient.unit))) + item_class = RecipeSummaryWithIngredients + else: + item_class = RecipeSummary q = q.options(*args) @@ -201,12 +211,13 @@ class RepositoryRecipes(RepositoryGeneric[Recipe, RecipeModel]): self.session.rollback() raise e - return RecipePagination( + items = [item_class.from_orm(item) for item in data] + return PaginationBase( page=pagination.page, per_page=pagination.per_page, total=count, total_pages=total_pages, - items=data, + items=items, ) def get_by_categories(self, categories: list[RecipeCategory]) -> list[RecipeSummary]: diff --git a/mealie/routes/recipe/recipe_crud_routes.py b/mealie/routes/recipe/recipe_crud_routes.py index 138a4135a6b8..0a578cda10d0 100644 --- a/mealie/routes/recipe/recipe_crud_routes.py +++ b/mealie/routes/recipe/recipe_crud_routes.py @@ -25,13 +25,21 @@ from mealie.routes._base.mixins import HttpRepo from mealie.routes._base.routers import MealieCrudRoute, UserAPIRouter from mealie.schema.cookbook.cookbook import ReadCookBook from mealie.schema.recipe import Recipe, RecipeImageTypes, ScrapeRecipe -from mealie.schema.recipe.recipe import CreateRecipe, CreateRecipeByUrlBulk, RecipePagination, RecipePaginationQuery +from mealie.schema.recipe.recipe import ( + CreateRecipe, + CreateRecipeByUrlBulk, + RecipePagination, + RecipePaginationQuery, + RecipeSummary, + RecipeSummaryWithIngredients, +) from mealie.schema.recipe.recipe_asset import RecipeAsset from mealie.schema.recipe.recipe_ingredient import RecipeIngredient from mealie.schema.recipe.recipe_scraper import ScrapeRecipeTest from mealie.schema.recipe.recipe_settings import RecipeSettings from mealie.schema.recipe.recipe_step import RecipeStep from mealie.schema.recipe.request_helpers import RecipeDuplicate, RecipeZipTokenResponse, UpdateImageResponse +from mealie.schema.response import PaginationBase from mealie.schema.response.responses import ErrorResponse from mealie.services import urls from mealie.services.event_bus_service.event_types import ( @@ -232,7 +240,7 @@ class RecipeController(BaseRecipeController): # ================================================================================================================== # CRUD Operations - @router.get("", response_model=RecipePagination) + @router.get("", response_model=PaginationBase[RecipeSummary | RecipeSummaryWithIngredients]) def get_all( self, request: Request, diff --git a/mealie/schema/recipe/recipe.py b/mealie/schema/recipe/recipe.py index 486001511049..f3b4c94df7bb 100644 --- a/mealie/schema/recipe/recipe.py +++ b/mealie/schema/recipe/recipe.py @@ -91,8 +91,6 @@ class RecipeSummary(MealieModel): rating: int | None org_url: str | None = Field(None, alias="orgURL") - recipe_ingredient: list[RecipeIngredient] | None = [] - date_added: datetime.date | None date_updated: datetime.datetime | None @@ -103,29 +101,9 @@ class RecipeSummary(MealieModel): class Config: orm_mode = True - @validator("tags", always=True, pre=True, allow_reuse=True) - def validate_tags(cats: list[Any]): # type: ignore - if isinstance(cats, list) and cats and isinstance(cats[0], str): - return [RecipeTag(id=uuid4(), name=c, slug=slugify(c)) for c in cats] - return cats - @validator("recipe_category", always=True, pre=True, allow_reuse=True) - def validate_categories(cats: list[Any]): # type: ignore - if isinstance(cats, list) and cats and isinstance(cats[0], str): - return [RecipeCategory(id=uuid4(), name=c, slug=slugify(c)) for c in cats] - return cats - - @validator("group_id", always=True, pre=True, allow_reuse=True) - def validate_group_id(group_id: Any): - if isinstance(group_id, int): - return uuid4() - return group_id - - @validator("user_id", always=True, pre=True, allow_reuse=True) - def validate_user_id(user_id: Any): - if isinstance(user_id, int): - return uuid4() - return user_id +class RecipeSummaryWithIngredients(RecipeSummary): + recipe_ingredient: list[RecipeIngredient] | None = [] class RecipePaginationQuery(PaginationQuery): @@ -205,8 +183,33 @@ class Recipe(RecipeSummary): return recipe_ingredient + @validator("tags", always=True, pre=True, allow_reuse=True) + def validate_tags(cats: list[Any]): # type: ignore + if isinstance(cats, list) and cats and isinstance(cats[0], str): + return [RecipeTag(id=uuid4(), name=c, slug=slugify(c)) for c in cats] + return cats + + @validator("recipe_category", always=True, pre=True, allow_reuse=True) + def validate_categories(cats: list[Any]): # type: ignore + if isinstance(cats, list) and cats and isinstance(cats[0], str): + return [RecipeCategory(id=uuid4(), name=c, slug=slugify(c)) for c in cats] + return cats + + @validator("group_id", always=True, pre=True, allow_reuse=True) + def validate_group_id(group_id: Any): + if isinstance(group_id, int): + return uuid4() + return group_id + + @validator("user_id", always=True, pre=True, allow_reuse=True) + def validate_user_id(user_id: Any): + if isinstance(user_id, int): + return uuid4() + return user_id + from mealie.schema.recipe.recipe_ingredient import RecipeIngredient # noqa: E402 RecipeSummary.update_forward_refs() +RecipeSummaryWithIngredients.update_forward_refs() Recipe.update_forward_refs()