From abb0b84e2223ea32e2550b9da83cf550606040a7 Mon Sep 17 00:00:00 2001 From: hay-kot Date: Mon, 23 Aug 2021 12:25:16 -0800 Subject: [PATCH] fix auto initialize models --- mealie/db/models/_model_base.py | 11 +++--- mealie/db/models/_model_utils.py | 22 +++++------ mealie/db/models/recipe/category.py | 26 ++++++------ mealie/db/models/recipe/ingredient.py | 41 ++++++------------- mealie/db/models/recipe/recipe.py | 57 +++++++++------------------ 5 files changed, 61 insertions(+), 96 deletions(-) diff --git a/mealie/db/models/_model_base.py b/mealie/db/models/_model_base.py index cea0ace32cec..cd33588d4f98 100644 --- a/mealie/db/models/_model_base.py +++ b/mealie/db/models/_model_base.py @@ -5,6 +5,7 @@ from mealie.db.db_setup import SessionLocal from sqlalchemy import Column, DateTime, Integer from sqlalchemy.ext.declarative import as_declarative from sqlalchemy.orm import declarative_base +from sqlalchemy.orm.session import Session def get_uuid_as_hex() -> str: @@ -38,15 +39,15 @@ class BaseMixins: self.__init__(*args, **kwarg) @classmethod - def get_ref(cls, match_value: str, match_attr: str = None): + def get_ref(cls, match_value: str, match_attr: str = None, session: Session = None): match_attr = match_attr = cls.Config.get_attr - if match_value is None: + if match_value is None or session is None: return None - with SessionLocal() as session: - eff_ref = getattr(cls, match_attr) - return session.query(cls).filter(eff_ref == match_value).one_or_none() + eff_ref = getattr(cls, match_attr) + + return session.query(cls).filter(eff_ref == match_value).one_or_none() SqlAlchemyBase = declarative_base(cls=Base, constructor=None) diff --git a/mealie/db/models/_model_utils.py b/mealie/db/models/_model_utils.py index 4a438675098d..cafb908c4a06 100644 --- a/mealie/db/models/_model_utils.py +++ b/mealie/db/models/_model_utils.py @@ -56,6 +56,8 @@ def auto_init(exclude: Union[set, list] = None): # sourcery no-metrics model_columns = self.__mapper__.columns relationships = self.__mapper__.relationships + session = kwargs.get("session", None) + for key, val in kwargs.items(): if key in exclude: continue @@ -86,24 +88,22 @@ def auto_init(exclude: Union[set, list] = None): # sourcery no-metrics val = val.get("id") if val is None: - raise ValueError( - f"Expected 'id' to be provided for {key}" - ) + raise ValueError(f"Expected 'id' to be provided for {key}") if isinstance(val, (str, int)): - instance = relation_cls.get_ref(match_value=val) + instance = relation_cls.get_ref(match_value=val, session=session) setattr(self, key, instance) elif relation_dir == MANYTOMANY.name: - if not isinstance(val, list): - raise ValueError( - f"Expected many to many input to be of type list for {key}" - ) - if isinstance(val[0], dict): + if not isinstance(val, list): + raise ValueError(f"Expected many to many input to be of type list for {key}") + + if len(val) > 0 and isinstance(val[0], dict): val = [elem.get("id") for elem in val] - intstances = [relation_cls.get_ref(elem) for elem in val] - setattr(self, key, intstances) + + instances = [relation_cls.get_ref(elem, session=session) for elem in val] + setattr(self, key, instances) return init(self, *args, **kwargs) diff --git a/mealie/db/models/recipe/category.py b/mealie/db/models/recipe/category.py index b5d8cb9d9fd1..c8334542caa0 100644 --- a/mealie/db/models/recipe/category.py +++ b/mealie/db/models/recipe/category.py @@ -1,9 +1,12 @@ +from functools import lru_cache + import sqlalchemy as sa import sqlalchemy.orm as orm from mealie.core import root_logger from mealie.db.db_setup import SessionLocal from mealie.db.models._model_base import BaseMixins, SqlAlchemyBase from slugify import slugify +from sqlalchemy import inspect from sqlalchemy.orm import validates logger = root_logger.get_logger() @@ -56,14 +59,15 @@ class Category(SqlAlchemyBase, BaseMixins): self.name = name.strip() self.slug = slugify(name) - @staticmethod - def create_if_not_exist(name: str = None): - test_slug = slugify(name) - with SessionLocal() as session: - result = session.query(Category).filter(Category.slug == test_slug).one_or_none() - if result: - logger.debug("Category exists, associating recipe") - return result - else: - logger.debug("Category doesn't exists, creating tag") - return Category(name=name) + @classmethod + def get_ref(cls, match_value: str, session=None): + if not session or not match_value: + return None + + result = session.query(Category).filter(Category.name == match_value).one_or_none() + if result: + logger.debug("Category exists, associating recipe") + return result + else: + logger.debug("Category doesn't exists, creating Category") + return Category(name=match_value) diff --git a/mealie/db/models/recipe/ingredient.py b/mealie/db/models/recipe/ingredient.py index 8ca2da961e26..7229d18a05c3 100644 --- a/mealie/db/models/recipe/ingredient.py +++ b/mealie/db/models/recipe/ingredient.py @@ -1,23 +1,8 @@ from mealie.db.models._model_base import BaseMixins, SqlAlchemyBase -from requests import Session -from sqlalchemy import Column, ForeignKey, Integer, String, Table, orm +from sqlalchemy import Column, ForeignKey, Integer, String, orm from .._model_utils import auto_init -ingredients_to_units = Table( - "ingredients_to_units", - SqlAlchemyBase.metadata, - Column("ingredient_units.id", Integer, ForeignKey("ingredient_units.id")), - Column("recipes_ingredients_id", Integer, ForeignKey("recipes_ingredients.id")), -) - -ingredients_to_foods = Table( - "ingredients_to_foods", - SqlAlchemyBase.metadata, - Column("ingredient_foods.id", Integer, ForeignKey("ingredient_foods.id")), - Column("recipes_ingredients_id", Integer, ForeignKey("recipes_ingredients.id")), -) - class IngredientUnitModel(SqlAlchemyBase, BaseMixins): __tablename__ = "ingredient_units" @@ -25,7 +10,7 @@ class IngredientUnitModel(SqlAlchemyBase, BaseMixins): name = Column(String) description = Column(String) abbreviation = Column(String) - ingredients = orm.relationship("RecipeIngredient", secondary=ingredients_to_units, back_populates="unit") + ingredients = orm.relationship("RecipeIngredient", back_populates="unit") @auto_init() def __init__(self, **_) -> None: @@ -37,7 +22,7 @@ class IngredientFoodModel(SqlAlchemyBase, BaseMixins): id = Column(Integer, primary_key=True) name = Column(String) description = Column(String) - ingredients = orm.relationship("RecipeIngredient", secondary=ingredients_to_foods, back_populates="food") + ingredients = orm.relationship("RecipeIngredient", back_populates="food") @auto_init() def __init__(self, **_) -> None: @@ -54,19 +39,15 @@ class RecipeIngredient(SqlAlchemyBase, BaseMixins): note = Column(String) # Force Show Text - Overrides Concat # Scaling Items - unit = orm.relationship(IngredientUnitModel, secondary=ingredients_to_units, uselist=False) - food = orm.relationship(IngredientFoodModel, secondary=ingredients_to_foods, uselist=False) + unit_id = Column(Integer, ForeignKey("ingredient_units.id")) + unit = orm.relationship(IngredientUnitModel, uselist=False) + + food_id = Column(Integer, ForeignKey("ingredient_foods.id")) + food = orm.relationship(IngredientFoodModel, uselist=False) quantity = Column(Integer) # Extras - def __init__(self, title: str, note: str, unit: dict, food: dict, quantity: int, session: Session, **_) -> None: - self.title = title - self.note = note - self.quantity = quantity - - if unit: - self.unit = IngredientUnitModel.get_ref(unit.get("id")) - - if food: - self.food = IngredientFoodModel.get_ref(unit.get("id")) + @auto_init() + def __init__(self, **_) -> None: + pass diff --git a/mealie/db/models/recipe/recipe.py b/mealie/db/models/recipe/recipe.py index 36677b8fe337..fd79fe72413f 100644 --- a/mealie/db/models/recipe/recipe.py +++ b/mealie/db/models/recipe/recipe.py @@ -7,6 +7,7 @@ from sqlalchemy.ext.orderinglist import ordering_list from sqlalchemy.orm import validates from .._model_base import BaseMixins, SqlAlchemyBase +from .._model_utils import auto_init from .api_extras import ApiExtras from .assets import RecipeAsset from .category import Category, recipes2categories @@ -77,66 +78,44 @@ class RecipeModel(SqlAlchemyBase, BaseMixins): assert name != "" return name + @auto_init( + { + "assets", + "extras", + "notes", + "nutrition", + "recipe_ingredient", + "recipe_instructions", + "settings", + "tools", + } + ) def __init__( self, session, - name: str = None, - description: str = None, - image: str = None, - recipe_yield: str = None, + assets: list = None, + extras: dict = None, + notes: list[dict] = None, + nutrition: dict = None, recipe_ingredient: list[str] = None, recipe_instructions: list[dict] = None, - recipeCuisine: str = None, - total_time: str = None, - prep_time: str = None, - cook_time: str = None, - nutrition: dict = None, - tools: list[str] = None, - perform_time: str = None, - slug: str = None, - recipe_category: list[str] = None, - tags: list[str] = None, - date_added: datetime.date = None, - notes: list[dict] = None, - rating: int = None, - org_url: str = None, - extras: dict = None, - assets: list = None, settings: dict = None, + tools: list[str] = None, **_ ) -> None: - self.name = name - self.description = description - self.image = image - self.recipeCuisine = recipeCuisine - self.nutrition = Nutrition(**nutrition) if self.nutrition else Nutrition() - self.tools = [Tool(tool=x) for x in tools] if tools else [] - - self.recipe_yield = recipe_yield self.recipe_ingredient = [RecipeIngredient(**ingr, session=session) for ingr in recipe_ingredient] self.assets = [RecipeAsset(**a) for a in assets] self.recipe_instructions = [ RecipeInstruction(text=instruc.get("text"), title=instruc.get("title"), type=instruc.get("@type", None)) for instruc in recipe_instructions ] - self.total_time = total_time - self.prep_time = prep_time - self.perform_time = perform_time - self.cook_time = cook_time - - self.recipe_category = [x for x in [Category.create_if_not_exist(cat) for cat in recipe_category] if x] # Mealie Specific self.settings = RecipeSettings(**settings) if settings else RecipeSettings() - self.tags = [Tag.create_if_not_exist(tag) for tag in tags] - self.slug = slug self.notes = [Note(**note) for note in notes] - self.rating = rating - self.org_url = org_url self.extras = [ApiExtras(key=key, value=value) for key, value in extras.items()] # Time Stampes - self.date_added = date_added self.date_updated = datetime.datetime.now()