fix auto initialize models

This commit is contained in:
hay-kot 2021-08-23 12:25:16 -08:00
parent 24a4b5b810
commit abb0b84e22
5 changed files with 61 additions and 96 deletions

View File

@ -5,6 +5,7 @@ from mealie.db.db_setup import SessionLocal
from sqlalchemy import Column, DateTime, Integer from sqlalchemy import Column, DateTime, Integer
from sqlalchemy.ext.declarative import as_declarative from sqlalchemy.ext.declarative import as_declarative
from sqlalchemy.orm import declarative_base from sqlalchemy.orm import declarative_base
from sqlalchemy.orm.session import Session
def get_uuid_as_hex() -> str: def get_uuid_as_hex() -> str:
@ -38,15 +39,15 @@ class BaseMixins:
self.__init__(*args, **kwarg) self.__init__(*args, **kwarg)
@classmethod @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 match_attr = match_attr = cls.Config.get_attr
if match_value is None: if match_value is None or session is None:
return None return None
with SessionLocal() as session: eff_ref = getattr(cls, match_attr)
eff_ref = getattr(cls, match_attr)
return session.query(cls).filter(eff_ref == match_value).one_or_none() return session.query(cls).filter(eff_ref == match_value).one_or_none()
SqlAlchemyBase = declarative_base(cls=Base, constructor=None) SqlAlchemyBase = declarative_base(cls=Base, constructor=None)

View File

@ -56,6 +56,8 @@ def auto_init(exclude: Union[set, list] = None): # sourcery no-metrics
model_columns = self.__mapper__.columns model_columns = self.__mapper__.columns
relationships = self.__mapper__.relationships relationships = self.__mapper__.relationships
session = kwargs.get("session", None)
for key, val in kwargs.items(): for key, val in kwargs.items():
if key in exclude: if key in exclude:
continue continue
@ -86,24 +88,22 @@ def auto_init(exclude: Union[set, list] = None): # sourcery no-metrics
val = val.get("id") val = val.get("id")
if val is None: if val is None:
raise ValueError( raise ValueError(f"Expected 'id' to be provided for {key}")
f"Expected 'id' to be provided for {key}"
)
if isinstance(val, (str, int)): 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) setattr(self, key, instance)
elif relation_dir == MANYTOMANY.name: 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] 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) return init(self, *args, **kwargs)

View File

@ -1,9 +1,12 @@
from functools import lru_cache
import sqlalchemy as sa import sqlalchemy as sa
import sqlalchemy.orm as orm import sqlalchemy.orm as orm
from mealie.core import root_logger from mealie.core import root_logger
from mealie.db.db_setup import SessionLocal from mealie.db.db_setup import SessionLocal
from mealie.db.models._model_base import BaseMixins, SqlAlchemyBase from mealie.db.models._model_base import BaseMixins, SqlAlchemyBase
from slugify import slugify from slugify import slugify
from sqlalchemy import inspect
from sqlalchemy.orm import validates from sqlalchemy.orm import validates
logger = root_logger.get_logger() logger = root_logger.get_logger()
@ -56,14 +59,15 @@ class Category(SqlAlchemyBase, BaseMixins):
self.name = name.strip() self.name = name.strip()
self.slug = slugify(name) self.slug = slugify(name)
@staticmethod @classmethod
def create_if_not_exist(name: str = None): def get_ref(cls, match_value: str, session=None):
test_slug = slugify(name) if not session or not match_value:
with SessionLocal() as session: return None
result = session.query(Category).filter(Category.slug == test_slug).one_or_none()
if result: result = session.query(Category).filter(Category.name == match_value).one_or_none()
logger.debug("Category exists, associating recipe") if result:
return result logger.debug("Category exists, associating recipe")
else: return result
logger.debug("Category doesn't exists, creating tag") else:
return Category(name=name) logger.debug("Category doesn't exists, creating Category")
return Category(name=match_value)

View File

@ -1,23 +1,8 @@
from mealie.db.models._model_base import BaseMixins, SqlAlchemyBase from mealie.db.models._model_base import BaseMixins, SqlAlchemyBase
from requests import Session from sqlalchemy import Column, ForeignKey, Integer, String, orm
from sqlalchemy import Column, ForeignKey, Integer, String, Table, orm
from .._model_utils import auto_init 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): class IngredientUnitModel(SqlAlchemyBase, BaseMixins):
__tablename__ = "ingredient_units" __tablename__ = "ingredient_units"
@ -25,7 +10,7 @@ class IngredientUnitModel(SqlAlchemyBase, BaseMixins):
name = Column(String) name = Column(String)
description = Column(String) description = Column(String)
abbreviation = 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() @auto_init()
def __init__(self, **_) -> None: def __init__(self, **_) -> None:
@ -37,7 +22,7 @@ class IngredientFoodModel(SqlAlchemyBase, BaseMixins):
id = Column(Integer, primary_key=True) id = Column(Integer, primary_key=True)
name = Column(String) name = Column(String)
description = 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() @auto_init()
def __init__(self, **_) -> None: def __init__(self, **_) -> None:
@ -54,19 +39,15 @@ class RecipeIngredient(SqlAlchemyBase, BaseMixins):
note = Column(String) # Force Show Text - Overrides Concat note = Column(String) # Force Show Text - Overrides Concat
# Scaling Items # Scaling Items
unit = orm.relationship(IngredientUnitModel, secondary=ingredients_to_units, uselist=False) unit_id = Column(Integer, ForeignKey("ingredient_units.id"))
food = orm.relationship(IngredientFoodModel, secondary=ingredients_to_foods, uselist=False) unit = orm.relationship(IngredientUnitModel, uselist=False)
food_id = Column(Integer, ForeignKey("ingredient_foods.id"))
food = orm.relationship(IngredientFoodModel, uselist=False)
quantity = Column(Integer) quantity = Column(Integer)
# Extras # Extras
def __init__(self, title: str, note: str, unit: dict, food: dict, quantity: int, session: Session, **_) -> None: @auto_init()
self.title = title def __init__(self, **_) -> None:
self.note = note pass
self.quantity = quantity
if unit:
self.unit = IngredientUnitModel.get_ref(unit.get("id"))
if food:
self.food = IngredientFoodModel.get_ref(unit.get("id"))

View File

@ -7,6 +7,7 @@ from sqlalchemy.ext.orderinglist import ordering_list
from sqlalchemy.orm import validates from sqlalchemy.orm import validates
from .._model_base import BaseMixins, SqlAlchemyBase from .._model_base import BaseMixins, SqlAlchemyBase
from .._model_utils import auto_init
from .api_extras import ApiExtras from .api_extras import ApiExtras
from .assets import RecipeAsset from .assets import RecipeAsset
from .category import Category, recipes2categories from .category import Category, recipes2categories
@ -77,66 +78,44 @@ class RecipeModel(SqlAlchemyBase, BaseMixins):
assert name != "" assert name != ""
return name return name
@auto_init(
{
"assets",
"extras",
"notes",
"nutrition",
"recipe_ingredient",
"recipe_instructions",
"settings",
"tools",
}
)
def __init__( def __init__(
self, self,
session, session,
name: str = None, assets: list = None,
description: str = None, extras: dict = None,
image: str = None, notes: list[dict] = None,
recipe_yield: str = None, nutrition: dict = None,
recipe_ingredient: list[str] = None, recipe_ingredient: list[str] = None,
recipe_instructions: list[dict] = 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, settings: dict = None,
tools: list[str] = None,
**_ **_
) -> None: ) -> None:
self.name = name
self.description = description
self.image = image
self.recipeCuisine = recipeCuisine
self.nutrition = Nutrition(**nutrition) if self.nutrition else Nutrition() self.nutrition = Nutrition(**nutrition) if self.nutrition else Nutrition()
self.tools = [Tool(tool=x) for x in tools] if tools else [] 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.recipe_ingredient = [RecipeIngredient(**ingr, session=session) for ingr in recipe_ingredient]
self.assets = [RecipeAsset(**a) for a in assets] self.assets = [RecipeAsset(**a) for a in assets]
self.recipe_instructions = [ self.recipe_instructions = [
RecipeInstruction(text=instruc.get("text"), title=instruc.get("title"), type=instruc.get("@type", None)) RecipeInstruction(text=instruc.get("text"), title=instruc.get("title"), type=instruc.get("@type", None))
for instruc in recipe_instructions 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 # Mealie Specific
self.settings = RecipeSettings(**settings) if settings else RecipeSettings() 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.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()] self.extras = [ApiExtras(key=key, value=value) for key, value in extras.items()]
# Time Stampes # Time Stampes
self.date_added = date_added
self.date_updated = datetime.datetime.now() self.date_updated = datetime.datetime.now()