mirror of
https://github.com/mealie-recipes/mealie.git
synced 2025-12-29 21:37:15 -05:00
feat: Cross-Household Recipes (#4089)
This commit is contained in:
@@ -32,6 +32,7 @@ from mealie.core.dependencies import (
|
||||
from mealie.core.security import create_recipe_slug_token
|
||||
from mealie.db.models.household.cookbook import CookBook
|
||||
from mealie.pkgs import cache
|
||||
from mealie.repos.all_repositories import get_repositories
|
||||
from mealie.repos.repository_generic import RepositoryGeneric
|
||||
from mealie.repos.repository_recipes import RepositoryRecipes
|
||||
from mealie.routes._base import BaseCrudController, controller
|
||||
@@ -94,9 +95,13 @@ class JSONBytes(JSONResponse):
|
||||
|
||||
class BaseRecipeController(BaseCrudController):
|
||||
@cached_property
|
||||
def repo(self) -> RepositoryRecipes:
|
||||
def recipes(self) -> RepositoryRecipes:
|
||||
return self.repos.recipes
|
||||
|
||||
@cached_property
|
||||
def group_recipes(self) -> RepositoryRecipes:
|
||||
return get_repositories(self.session, group_id=self.group_id, household_id=None).recipes
|
||||
|
||||
@cached_property
|
||||
def cookbooks_repo(self) -> RepositoryGeneric[ReadCookBook, CookBook]:
|
||||
return self.repos.cookbooks
|
||||
@@ -107,7 +112,7 @@ class BaseRecipeController(BaseCrudController):
|
||||
|
||||
@cached_property
|
||||
def mixins(self):
|
||||
return HttpRepo[CreateRecipe, Recipe, Recipe](self.repo, self.logger)
|
||||
return HttpRepo[CreateRecipe, Recipe, Recipe](self.recipes, self.logger)
|
||||
|
||||
|
||||
class FormatResponse(BaseModel):
|
||||
@@ -331,8 +336,9 @@ class RecipeController(BaseRecipeController):
|
||||
if cookbook_data is None:
|
||||
raise HTTPException(status_code=404, detail="cookbook not found")
|
||||
|
||||
# we use the repo by user so we can sort favorites correctly
|
||||
pagination_response = self.repos.recipes.by_user(self.user.id).page_all(
|
||||
# We use "group_recipes" here so we can return all recipes regardless of household. The query filter can include
|
||||
# a household_id to filter by household. We use the "by_user" so we can sort favorites correctly.
|
||||
pagination_response = self.group_recipes.by_user(self.user.id).page_all(
|
||||
pagination=q,
|
||||
cookbook=cookbook_data,
|
||||
categories=categories,
|
||||
@@ -362,7 +368,7 @@ class RecipeController(BaseRecipeController):
|
||||
def get_one(self, slug: str = Path(..., description="A recipe's slug or id")):
|
||||
"""Takes in a recipe's slug or id and returns all data for a recipe"""
|
||||
try:
|
||||
recipe = self.service.get_one_by_slug_or_id(slug)
|
||||
recipe = self.service.get_one(slug)
|
||||
except Exception as e:
|
||||
self.handle_exceptions(e)
|
||||
return None
|
||||
@@ -534,7 +540,7 @@ class RecipeController(BaseRecipeController):
|
||||
data_service = RecipeDataService(recipe.id)
|
||||
data_service.write_image(image, extension)
|
||||
|
||||
new_version = self.repo.update_image(slug, extension)
|
||||
new_version = self.recipes.update_image(slug, extension)
|
||||
return UpdateImageResponse(image=new_version)
|
||||
|
||||
@router.post("/{slug}/assets", response_model=RecipeAsset, tags=["Recipe: Images and Assets"])
|
||||
|
||||
@@ -4,6 +4,7 @@ from functools import cached_property
|
||||
from fastapi import Depends, File, Form, HTTPException
|
||||
from pydantic import UUID4
|
||||
|
||||
from mealie.repos.all_repositories import get_repositories
|
||||
from mealie.routes._base import BaseCrudController, controller
|
||||
from mealie.routes._base.mixins import HttpRepo
|
||||
from mealie.routes._base.routers import MealieCrudRoute, UserAPIRouter
|
||||
@@ -31,8 +32,8 @@ class RecipeTimelineEventsController(BaseCrudController):
|
||||
return self.repos.recipe_timeline_events
|
||||
|
||||
@cached_property
|
||||
def recipes_repo(self):
|
||||
return self.repos.recipes
|
||||
def group_recipes(self):
|
||||
return get_repositories(self.session, group_id=self.group_id, household_id=None).recipes
|
||||
|
||||
@cached_property
|
||||
def mixins(self):
|
||||
@@ -57,7 +58,7 @@ class RecipeTimelineEventsController(BaseCrudController):
|
||||
# if the user id is not specified, use the currently-authenticated user
|
||||
data.user_id = data.user_id or self.user.id
|
||||
|
||||
recipe = self.recipes_repo.get_one(data.recipe_id, "id")
|
||||
recipe = self.group_recipes.get_one(data.recipe_id, "id")
|
||||
if not recipe:
|
||||
raise HTTPException(status_code=404, detail="recipe not found")
|
||||
|
||||
@@ -87,7 +88,7 @@ class RecipeTimelineEventsController(BaseCrudController):
|
||||
@events_router.put("/{item_id}", response_model=RecipeTimelineEventOut)
|
||||
def update_one(self, item_id: UUID4, data: RecipeTimelineEventUpdate):
|
||||
event = self.mixins.patch_one(data, item_id)
|
||||
recipe = self.recipes_repo.get_one(event.recipe_id, "id")
|
||||
recipe = self.group_recipes.get_one(event.recipe_id, "id")
|
||||
if recipe:
|
||||
self.publish_event(
|
||||
event_type=EventTypes.recipe_updated,
|
||||
@@ -114,7 +115,7 @@ class RecipeTimelineEventsController(BaseCrudController):
|
||||
except FileNotFoundError:
|
||||
pass
|
||||
|
||||
recipe = self.recipes_repo.get_one(event.recipe_id, "id")
|
||||
recipe = self.group_recipes.get_one(event.recipe_id, "id")
|
||||
if recipe:
|
||||
self.publish_event(
|
||||
event_type=EventTypes.recipe_updated,
|
||||
@@ -144,7 +145,7 @@ class RecipeTimelineEventsController(BaseCrudController):
|
||||
if event.image != TimelineEventImage.has_image.value:
|
||||
event.image = TimelineEventImage.has_image
|
||||
event = self.mixins.patch_one(event.cast(RecipeTimelineEventUpdate), event.id)
|
||||
recipe = self.recipes_repo.get_one(event.recipe_id, "id")
|
||||
recipe = self.group_recipes.get_one(event.recipe_id, "id")
|
||||
if recipe:
|
||||
self.publish_event(
|
||||
event_type=EventTypes.recipe_updated,
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
from functools import cached_property
|
||||
from uuid import UUID
|
||||
|
||||
from fastapi import HTTPException, status
|
||||
from pydantic import UUID4
|
||||
|
||||
from mealie.repos.all_repositories import get_repositories
|
||||
from mealie.routes._base import BaseUserController, controller
|
||||
from mealie.routes._base.routers import UserAPIRouter
|
||||
from mealie.routes.users._helpers import assert_user_change_allowed
|
||||
@@ -14,6 +16,10 @@ router = UserAPIRouter()
|
||||
|
||||
@controller(router)
|
||||
class UserRatingsController(BaseUserController):
|
||||
@cached_property
|
||||
def group_recipes(self):
|
||||
return get_repositories(self.session, group_id=self.group_id, household_id=None).recipes
|
||||
|
||||
def get_recipe_or_404(self, slug_or_id: str | UUID):
|
||||
"""Fetches a recipe by slug or id, or raises a 404 error if not found."""
|
||||
if isinstance(slug_or_id, str):
|
||||
@@ -22,11 +28,10 @@ class UserRatingsController(BaseUserController):
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
recipes_repo = self.repos.recipes
|
||||
if isinstance(slug_or_id, UUID):
|
||||
recipe = recipes_repo.get_one(slug_or_id, key="id")
|
||||
recipe = self.group_recipes.get_one(slug_or_id, key="id")
|
||||
else:
|
||||
recipe = recipes_repo.get_one(slug_or_id, key="slug")
|
||||
recipe = self.group_recipes.get_one(slug_or_id, key="slug")
|
||||
|
||||
if not recipe:
|
||||
raise HTTPException(
|
||||
|
||||
Reference in New Issue
Block a user