fix: #6802 prevent 500 internal server error when patching recipe tags (#6803)

Co-authored-by: Michael Genson <71845777+michael-genson@users.noreply.github.com>
Co-authored-by: Michael Genson <genson.michael@gmail.com>
This commit is contained in:
Sim
2026-03-18 18:45:51 +00:00
committed by GitHub
parent c533da1c21
commit 26924ab054
3 changed files with 57 additions and 2 deletions

View File

@@ -9,7 +9,7 @@ def api_extras(func):
"""Decorator function to unpack the extras into a dict; requires an "extras" column""" """Decorator function to unpack the extras into a dict; requires an "extras" column"""
def wrapper(*args, **kwargs): def wrapper(*args, **kwargs):
extras = kwargs.pop("extras") extras = kwargs.pop("extras", None)
if extras is None: if extras is None:
extras = [] extras = []

View File

@@ -201,6 +201,16 @@ class RepositoryRecipes(HouseholdRepositoryGeneric[Recipe, RecipeModel]):
additional_ids = self.session.execute(sa.select(model.id).filter(model.slug.in_(slugs))).scalars().all() additional_ids = self.session.execute(sa.select(model.id).filter(model.slug.in_(slugs))).scalars().all()
return ids + additional_ids return ids + additional_ids
def update(self, match_value: str | int | UUID4, new_data: dict | Recipe) -> Recipe:
new_data = new_data if isinstance(new_data, dict) else new_data.model_dump()
# Handle explicit group_id injection for related items that require it
for organizer_field in ["tags", "recipe_category", "tools"]:
for organizer in new_data.get(organizer_field, []):
organizer["group_id"] = self.group_id
return super().update(match_value, new_data)
def page_all( # type: ignore def page_all( # type: ignore
self, self,
pagination: PaginationQuery, pagination: PaginationQuery,

View File

@@ -1,5 +1,5 @@
from datetime import UTC, datetime, timedelta from datetime import UTC, datetime, timedelta
from uuid import UUID from uuid import UUID, uuid4
import pytest import pytest
from sqlalchemy.orm import Session from sqlalchemy.orm import Session
@@ -810,3 +810,48 @@ def test_order_by_rating(user_tuple: tuple[TestUser, TestUser]):
assert data[0].slug == recipe_2.slug # global rating == 2.5 (avg of 4 and 1) assert data[0].slug == recipe_2.slug # global rating == 2.5 (avg of 4 and 1)
assert data[1].slug == recipe_3.slug # global rating == 3 assert data[1].slug == recipe_3.slug # global rating == 3
assert data[2].slug == recipe_1.slug # global rating == 4.25 (avg of 5 and 3.5) assert data[2].slug == recipe_1.slug # global rating == 4.25 (avg of 5 and 3.5)
@pytest.mark.parametrize("route", ["patch", "update"])
def test_recipe_inject_organizer_group_id(unique_user: TestUser, route: str):
# Regression test for #6802 - Ensure explicit group_id injection works for new organizers
database = unique_user.repos
recipe = database.recipes.create(
Recipe(
user_id=unique_user.user_id,
group_id=unique_user.group_id,
name=random_string(),
)
)
new_tag_name = random_string()
new_cat_name = random_string()
new_tool_name = random_string()
patch_data = Recipe(
name=recipe.name,
tags=[new_tag_name],
recipe_category=[new_cat_name],
tools=[{"id": uuid4(), "name": new_tool_name, "slug": new_tool_name}],
)
# This should not raise IntegrityError or TypeError
update_func = database.recipes.patch if route == "patch" else database.recipes.update
if route == "patch":
updated_recipe = update_func(recipe.slug, patch_data.model_dump(exclude_unset=True))
elif route == "update":
updated_recipe = update_func(recipe.slug, patch_data.model_dump(exclude_unset=True))
assert updated_recipe
assert updated_recipe.tags
assert len(updated_recipe.tags) == 1
assert updated_recipe.tags[0].name == new_tag_name
assert updated_recipe.recipe_category
assert len(updated_recipe.recipe_category) == 1
assert updated_recipe.recipe_category[0].name == new_cat_name
assert updated_recipe.tools
assert len(updated_recipe.tools) == 1
assert updated_recipe.tools[0].name == new_tool_name