feat: Add recipe as ingredient (#4800)

Co-authored-by: Michael Genson <71845777+michael-genson@users.noreply.github.com>
This commit is contained in:
gpotter@gmail.com
2025-11-03 21:57:57 -08:00
committed by GitHub
parent ff42964537
commit 60d9294861
27 changed files with 1037 additions and 80 deletions

View File

@@ -20,6 +20,7 @@ from mealie.schema.household.group_shopping_list import (
ShoppingListOut,
ShoppingListSave,
)
from mealie.schema.recipe.recipe import Recipe
from mealie.schema.recipe.recipe_ingredient import (
IngredientFood,
IngredientUnit,
@@ -315,10 +316,22 @@ class ShoppingListService:
list_items: list[ShoppingListItemCreate] = []
for ingredient in recipe_ingredients:
if isinstance(ingredient.referenced_recipe, Recipe):
# Recursively process sub-recipe ingredients
sub_recipe = ingredient.referenced_recipe
sub_scale = (ingredient.quantity or 1) * scale
sub_items = self.get_shopping_list_items_from_recipe(
list_id,
sub_recipe.id,
sub_scale,
sub_recipe.recipe_ingredient,
)
list_items.extend(sub_items)
continue
if isinstance(ingredient.food, IngredientFood):
food_id = ingredient.food.id
label_id = ingredient.food.label_id
else:
food_id = None
label_id = None

View File

@@ -369,6 +369,27 @@ class RecipeService(RecipeServiceBase):
return new_recipe
def has_recursive_recipe_link(self, recipe: Recipe, visited: set[str] | None = None):
"""Recursively checks if a recipe links to itself through its ingredients."""
if visited is None:
visited = set()
recipe_id = str(getattr(recipe, "id", None))
if recipe_id in visited:
return True
visited.add(recipe_id)
ingredients = getattr(recipe, "recipe_ingredient", [])
for ing in ingredients:
try:
sub_recipe = self.get_one(ing.referenced_recipe.id)
except (AttributeError, exceptions.NoEntryFound):
continue
if self.has_recursive_recipe_link(sub_recipe, visited):
return True
return False
def _pre_update_check(self, slug_or_id: str | UUID, new_data: Recipe) -> Recipe:
"""
gets the recipe from the database and performs a check to see if the user can update the recipe.
@@ -399,6 +420,9 @@ class RecipeService(RecipeServiceBase):
if setting_lock and not self.can_lock_unlock(recipe):
raise exceptions.PermissionDenied("You do not have permission to lock/unlock this recipe.")
if self.has_recursive_recipe_link(new_data):
raise exceptions.RecursiveRecipe("Recursive recipe link detected. Update aborted.")
return recipe
def update_one(self, slug_or_id: str | UUID, update_data: Recipe) -> Recipe: