mirror of
				https://github.com/mealie-recipes/mealie.git
				synced 2025-11-03 18:53:17 -05:00 
			
		
		
		
	fix auto initialize models
This commit is contained in:
		@@ -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,14 +39,14 @@ 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()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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()
 | 
					
 | 
				
			||||||
 | 
					        result = session.query(Category).filter(Category.name == match_value).one_or_none()
 | 
				
			||||||
        if result:
 | 
					        if result:
 | 
				
			||||||
            logger.debug("Category exists, associating recipe")
 | 
					            logger.debug("Category exists, associating recipe")
 | 
				
			||||||
            return result
 | 
					            return result
 | 
				
			||||||
        else:
 | 
					        else:
 | 
				
			||||||
                logger.debug("Category doesn't exists, creating tag")
 | 
					            logger.debug("Category doesn't exists, creating Category")
 | 
				
			||||||
                return Category(name=name)
 | 
					            return Category(name=match_value)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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"))
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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()
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user