mirror of
				https://github.com/mealie-recipes/mealie.git
				synced 2025-10-30 17:53:31 -04:00 
			
		
		
		
	fix performance issues on /api/foods (#2163)
* fix performance issues on /api/foods * fix comment * actually apply query-options
This commit is contained in:
		| @@ -1,5 +1,7 @@ | ||||
| from pydantic import UUID4 | ||||
| from sqlalchemy import select | ||||
| from sqlalchemy.orm import joinedload | ||||
| from sqlalchemy.orm.interfaces import LoaderOption | ||||
|  | ||||
| from mealie.db.models.recipe.ingredient import IngredientFoodModel | ||||
| from mealie.schema.recipe.recipe_ingredient import IngredientFood | ||||
| @@ -29,3 +31,9 @@ class RepositoryFood(RepositoryGeneric[IngredientFood, IngredientFoodModel]): | ||||
|  | ||||
|     def by_group(self, group_id: UUID4) -> "RepositoryFood": | ||||
|         return super().by_group(group_id) | ||||
|  | ||||
|     def paging_query_options(self) -> list[LoaderOption]: | ||||
|         return [ | ||||
|             joinedload(IngredientFoodModel.extras), | ||||
|             joinedload(IngredientFoodModel.label), | ||||
|         ] | ||||
|   | ||||
| @@ -7,6 +7,7 @@ from typing import Any, Generic, TypeVar | ||||
| from fastapi import HTTPException | ||||
| from pydantic import UUID4, BaseModel | ||||
| from sqlalchemy import Select, delete, func, select | ||||
| from sqlalchemy.orm.interfaces import LoaderOption | ||||
| from sqlalchemy.orm.session import Session | ||||
| from sqlalchemy.sql import sqltypes | ||||
|  | ||||
| @@ -284,6 +285,10 @@ class RepositoryGeneric(Generic[Schema, Model]): | ||||
|             q = self._query().filter(attribute_name == attr_match) | ||||
|             return [eff_schema.from_orm(x) for x in self.session.execute(q).scalars().all()] | ||||
|  | ||||
|     def paging_query_options(self) -> list[LoaderOption]: | ||||
|         # Override this in subclasses to specify joinedloads or similar for page_all | ||||
|         return [] | ||||
|  | ||||
|     def page_all(self, pagination: PaginationQuery, override=None) -> PaginationBase[Schema]: | ||||
|         """ | ||||
|         pagination is a method to interact with the filtered database table and return a paginated result | ||||
| @@ -296,19 +301,18 @@ class RepositoryGeneric(Generic[Schema, Model]): | ||||
|         """ | ||||
|         eff_schema = override or self.schema | ||||
|  | ||||
|         q = self._query() | ||||
|         q = self._query().options(*self.paging_query_options()) | ||||
|  | ||||
|         fltr = self._filter_builder() | ||||
|         q = q.filter_by(**fltr) | ||||
|         q, count, total_pages = self.add_pagination_to_query(q, pagination) | ||||
|  | ||||
|         try: | ||||
|             data = self.session.execute(q).scalars().all() | ||||
|             data = self.session.execute(q).unique().scalars().all() | ||||
|         except Exception as e: | ||||
|             self._log_exception(e) | ||||
|             self.session.rollback() | ||||
|             raise e | ||||
|  | ||||
|         return PaginationBase( | ||||
|             page=pagination.page, | ||||
|             per_page=pagination.per_page, | ||||
|   | ||||
| @@ -2,12 +2,12 @@ from __future__ import annotations | ||||
|  | ||||
| import datetime | ||||
| import enum | ||||
| from typing import Any | ||||
| from uuid import UUID, uuid4 | ||||
|  | ||||
| from pydantic import UUID4, Field, validator | ||||
| from pydantic.utils import GetterDict | ||||
|  | ||||
| from mealie.db.models.recipe.ingredient import IngredientFoodModel | ||||
| from mealie.schema._mealie import MealieModel | ||||
| from mealie.schema._mealie.types import NoneFloat | ||||
| from mealie.schema.response.pagination import PaginationBase | ||||
| @@ -37,14 +37,19 @@ class IngredientFood(CreateIngredientFood): | ||||
|     update_at: datetime.datetime | None | ||||
|  | ||||
|     class Config: | ||||
|         orm_mode = True | ||||
|         class _FoodGetter(GetterDict): | ||||
|             def get(self, key: Any, default: Any = None) -> Any: | ||||
|                 # Transform extras into key-value dict | ||||
|                 if key == "extras": | ||||
|                     value = super().get(key, default) | ||||
|                     return {x.key_name: x.value for x in value} | ||||
|  | ||||
|         @classmethod | ||||
|         def getter_dict(cls, name_orm: IngredientFoodModel): | ||||
|             return { | ||||
|                 **GetterDict(name_orm), | ||||
|                 "extras": {x.key_name: x.value for x in name_orm.extras}, | ||||
|             } | ||||
|                 # Keep all other fields as they are | ||||
|                 else: | ||||
|                     return super().get(key, default) | ||||
|  | ||||
|         orm_mode = True | ||||
|         getter_dict = _FoodGetter | ||||
|  | ||||
|  | ||||
| class IngredientFoodPagination(PaginationBase): | ||||
|   | ||||
		Reference in New Issue
	
	Block a user