diff --git a/mealie/db/models/_model_base.py b/mealie/db/models/_model_base.py index 6f44b608bae..99dc2b53269 100644 --- a/mealie/db/models/_model_base.py +++ b/mealie/db/models/_model_base.py @@ -20,5 +20,11 @@ class BaseMixins: `self.update` method which directly passing arguments to the `__init__` """ - def update(self, *args, **kwarg): - self.__init__(*args, **kwarg) + def update(self, *args, **kwargs): + self.__init__(*args, **kwargs) + + # sqlalchemy doesn't like this method to remove all instances of a 1:many relationship, + # so we explicitly check for that here + for k, v in kwargs.items(): + if hasattr(self, k) and v == []: + setattr(self, k, v) diff --git a/tests/integration_tests/user_recipe_tests/test_recipe_crud.py b/tests/integration_tests/user_recipe_tests/test_recipe_crud.py index e3c8b33b417..273bb7ec13e 100644 --- a/tests/integration_tests/user_recipe_tests/test_recipe_crud.py +++ b/tests/integration_tests/user_recipe_tests/test_recipe_crud.py @@ -18,6 +18,7 @@ from mealie.repos.repository_factory import AllRepositories from mealie.schema.recipe.recipe import RecipeCategory, RecipeSummary, RecipeTag +from mealie.schema.recipe.recipe_notes import RecipeNote from mealie.services.recipe.recipe_data_service import RecipeDataService from mealie.services.scraper.recipe_scraper import DEFAULT_SCRAPER_STRATEGIES from tests import data, utils @@ -610,6 +611,42 @@ def test_rename(api_client: TestClient, recipe_data: RecipeSiteTestCase, unique_ recipe_data.expected_slug = new_slug +def test_remove_notes(api_client: TestClient, unique_user: TestUser): + # create recipe + recipe_create_url = api_routes.recipes + recipe_create_data = {"name": random_string()} + response = api_client.post(recipe_create_url, headers=unique_user.token, json=recipe_create_data) + assert response.status_code == 201 + recipe_slug: str = response.json() + + # get recipe and add a note + recipe_url = api_routes.recipes_slug(recipe_slug) + response = api_client.get(recipe_url, headers=unique_user.token) + assert response.status_code == 200 + + recipe = json.loads(response.text) + recipe["notes"] = [RecipeNote(title=random_string(), text=random_string()).dict()] + response = api_client.put(recipe_url, json=recipe, headers=unique_user.token) + assert response.status_code == 200 + + # get recipe and remove the note + response = api_client.get(recipe_url, headers=unique_user.token) + assert response.status_code == 200 + + recipe = json.loads(response.text) + assert len(recipe.get("notes", [])) == 1 + recipe["notes"] = [] + response = api_client.put(recipe_url, json=recipe, headers=unique_user.token) + assert response.status_code == 200 + + # verify the note is removed + response = api_client.get(recipe_url, headers=unique_user.token) + assert response.status_code == 200 + + recipe = json.loads(response.text) + assert len(recipe.get("notes", [])) == 0 + + @pytest.mark.parametrize("recipe_data", recipe_test_data) def test_delete(api_client: TestClient, recipe_data: RecipeSiteTestCase, unique_user: TestUser): response = api_client.delete(api_routes.recipes_slug(recipe_data.expected_slug), headers=unique_user.token)