Remove all sqlalchemy lazy-loading from app (#2260)

* Remove some implicit lazy-loads from user serialization

* implement full backup restore across different database versions

* rework all custom getter dicts to not leak lazy loads

* remove some occurances of lazy-loading

* remove a lot of lazy loading from recipes

* add more eager loading
remove loading options from repository
remove raiseload for checking

* fix failing test

* do not apply loader options for paging counts

* try using selectinload a bit more instead of joinedload

* linter fixes
This commit is contained in:
Sören
2023-03-24 17:27:26 +01:00
committed by GitHub
parent fae62ecb19
commit 4b426ddf2f
23 changed files with 351 additions and 142 deletions

View File

@@ -1,5 +1,8 @@
from pydantic import UUID4, NoneStr
from sqlalchemy.orm import joinedload
from sqlalchemy.orm.interfaces import LoaderOption
from mealie.db.models.group import GroupEventNotifierModel
from mealie.schema._mealie import MealieModel
from mealie.schema.response.pagination import PaginationBase
@@ -86,6 +89,10 @@ class GroupEventNotifierOut(MealieModel):
class Config:
orm_mode = True
@classmethod
def loader_options(cls) -> list[LoaderOption]:
return [joinedload(GroupEventNotifierModel.options)]
class GroupEventPagination(PaginationBase):
items: list[GroupEventNotifierOut]

View File

@@ -4,11 +4,19 @@ from datetime import datetime
from fractions import Fraction
from pydantic import UUID4, validator
from pydantic.utils import GetterDict
from sqlalchemy.orm import joinedload, selectinload
from sqlalchemy.orm.interfaces import LoaderOption
from mealie.db.models.group.shopping_list import ShoppingList, ShoppingListItem
from mealie.db.models.group import (
ShoppingList,
ShoppingListItem,
ShoppingListMultiPurposeLabel,
ShoppingListRecipeReference,
)
from mealie.db.models.recipe import IngredientFoodModel, RecipeModel
from mealie.schema._mealie import MealieModel
from mealie.schema._mealie.types import NoneFloat
from mealie.schema.getter_dict import ExtrasGetterDict
from mealie.schema.labels.multi_purpose_label import MultiPurposeLabelSummary
from mealie.schema.recipe.recipe import RecipeSummary
from mealie.schema.recipe.recipe_ingredient import (
@@ -171,13 +179,18 @@ class ShoppingListItemOut(ShoppingListItemBase):
class Config:
orm_mode = True
getter_dict = ExtrasGetterDict
@classmethod
def getter_dict(cls, name_orm: ShoppingListItem):
return {
**GetterDict(name_orm),
"extras": {x.key_name: x.value for x in name_orm.extras},
}
@classmethod
def loader_options(cls) -> list[LoaderOption]:
return [
selectinload(ShoppingListItem.extras),
selectinload(ShoppingListItem.food).joinedload(IngredientFoodModel.extras),
selectinload(ShoppingListItem.food).joinedload(IngredientFoodModel.label),
joinedload(ShoppingListItem.label),
joinedload(ShoppingListItem.unit),
selectinload(ShoppingListItem.recipe_references),
]
class ShoppingListItemsCollectionOut(MealieModel):
@@ -204,6 +217,10 @@ class ShoppingListMultiPurposeLabelOut(ShoppingListMultiPurposeLabelUpdate):
class Config:
orm_mode = True
@classmethod
def loader_options(cls) -> list[LoaderOption]:
return [joinedload(ShoppingListMultiPurposeLabel.label)]
class ShoppingListItemPagination(PaginationBase):
items: list[ShoppingListItemOut]
@@ -229,6 +246,14 @@ class ShoppingListRecipeRefOut(MealieModel):
class Config:
orm_mode = True
@classmethod
def loader_options(cls) -> list[LoaderOption]:
return [
selectinload(ShoppingListRecipeReference.recipe).joinedload(RecipeModel.recipe_category),
selectinload(ShoppingListRecipeReference.recipe).joinedload(RecipeModel.tags),
selectinload(ShoppingListRecipeReference.recipe).joinedload(RecipeModel.tools),
]
class ShoppingListSave(ShoppingListCreate):
group_id: UUID4
@@ -241,13 +266,23 @@ class ShoppingListSummary(ShoppingListSave):
class Config:
orm_mode = True
getter_dict = ExtrasGetterDict
@classmethod
def getter_dict(cls, name_orm: ShoppingList):
return {
**GetterDict(name_orm),
"extras": {x.key_name: x.value for x in name_orm.extras},
}
@classmethod
def loader_options(cls) -> list[LoaderOption]:
return [
selectinload(ShoppingList.extras),
selectinload(ShoppingList.recipe_references)
.joinedload(ShoppingListRecipeReference.recipe)
.joinedload(RecipeModel.recipe_category),
selectinload(ShoppingList.recipe_references)
.joinedload(ShoppingListRecipeReference.recipe)
.joinedload(RecipeModel.tags),
selectinload(ShoppingList.recipe_references)
.joinedload(ShoppingListRecipeReference.recipe)
.joinedload(RecipeModel.tools),
selectinload(ShoppingList.label_settings).joinedload(ShoppingListMultiPurposeLabel.label),
]
class ShoppingListPagination(PaginationBase):
@@ -265,13 +300,33 @@ class ShoppingListOut(ShoppingListUpdate):
class Config:
orm_mode = True
getter_dict = ExtrasGetterDict
@classmethod
def getter_dict(cls, name_orm: ShoppingList):
return {
**GetterDict(name_orm),
"extras": {x.key_name: x.value for x in name_orm.extras},
}
@classmethod
def loader_options(cls) -> list[LoaderOption]:
return [
selectinload(ShoppingList.extras),
selectinload(ShoppingList.list_items).joinedload(ShoppingListItem.extras),
selectinload(ShoppingList.list_items)
.joinedload(ShoppingListItem.food)
.joinedload(IngredientFoodModel.extras),
selectinload(ShoppingList.list_items)
.joinedload(ShoppingListItem.food)
.joinedload(IngredientFoodModel.label),
selectinload(ShoppingList.list_items).joinedload(ShoppingListItem.label),
selectinload(ShoppingList.list_items).joinedload(ShoppingListItem.unit),
selectinload(ShoppingList.list_items).joinedload(ShoppingListItem.recipe_references),
selectinload(ShoppingList.recipe_references)
.joinedload(ShoppingListRecipeReference.recipe)
.joinedload(RecipeModel.recipe_category),
selectinload(ShoppingList.recipe_references)
.joinedload(ShoppingListRecipeReference.recipe)
.joinedload(RecipeModel.tags),
selectinload(ShoppingList.recipe_references)
.joinedload(ShoppingListRecipeReference.recipe)
.joinedload(RecipeModel.tools),
selectinload(ShoppingList.label_settings).joinedload(ShoppingListMultiPurposeLabel.label),
]
class ShoppingListAddRecipeParams(MealieModel):