mealie-recipes / mealie

Mealie is a self hosted recipe manager and meal planner with a RestAPI backend and a reactive frontend application built in Vue for a pleasant user experience for the whole family. Easily add recipes into your database by providing the url and mealie will automatically import the relevant data or add a family recipe with the UI editor
https://docs.mealie.io
GNU Affero General Public License v3.0
7.53k stars 754 forks source link

[v1.0.0b] - __init__() missing 1 required positional argument: 'group_id' #2417

Closed jakguru closed 4 hours ago

jakguru commented 1 year ago

First Check

What is the issue you are experiencing?

I am attempting to create a new recipe using the API. I first parse the ingredients via the ingredients parser using the /api/parser/ingredients endpoint, and then create the recipe using the /api/recipes endpoint.

When i then try to update the recipe via the PUT /api/recipes/{slug} endpoint with the following payload:

{
  "uuid": "ht60f6fffaf84ab920df8eda6c",
  "userId": "422299a7-9939-4d8c-92d6-ecfb5bb3bd71",
  "groupId": "6707cc54-2ed2-492d-bc8f-5c31b612b58f",
  "name": "Sausage Mixed Grill",
  "slug": "sausage-mixed-grill",
  "image": "https://www.harristeeter.com//content/v2/binary/recipe/images/ht60f6fffaf84ab920df8eda6c-original.jpg",
  "recipeYield": "4",
  "totalTime": "30",
  "prepTime": "10",
  "cookTime": "20",
  "performTime": null,
  "description": "<p>Your next cookout just got a lot easier – with this Sausage Mixed Grill recipe.  Combine an assortment of your favorite sausages and serve them with a cold lager.  With a clean taste and light malty sweetness, a lager beer perfectly complements this lightly browned and grilled supper. Pair with a lager, such as Kona Longboard Island Lager or Samuel Adams Wicked Easy Light and Hazy Lager</p>",
  "recipeCategory": [],
  "tags": [
    "Dinner",
    "Pork",
    "Chicken & Poultry",
    "Beef",
    "Pork & Ham",
    "Savory",
    "Easy",
    "4th of July",
    "Dinner Party",
    "Game Day",
    "Memorial Day",
    "Fathers Day",
    "Party Food",
    "Labor Day",
    "Cookout & Grilling",
    "Grill & Smoke",
    "Assemble",
    "American",
    "Entrées"
  ],
  "tools": [],
  "rating": null,
  "orgURL": "https://www.harristeeter.com/recipes/sausage-mixed-grill",
  "recipeIngredient": [
    {
      "input": "16 multi-colored mini bell peppers",
      "confidence": {
        "average": 0.83,
        "comment": null,
        "name": 0.68,
        "unit": null,
        "quantity": 0.98,
        "food": 0.68
      },
      "ingredient": {
        "title": "",
        "note": "",
        "unit": {
          "name": "",
          "description": "",
          "extras": {},
          "fraction": true,
          "abbreviation": "",
          "useAbbreviation": false
        },
        "food": {
          "name": "multi-colored mini bell peppers",
          "description": "",
          "extras": {},
          "labelId": null
        },
        "disableAmount": false,
        "quantity": 16,
        "originalText": null,
        "referenceId": "1c0f78f3-c5b3-4721-969f-61c88cc40578"
      }
    },
    {
      "input": "12 shishito peppers",
      "confidence": {
        "average": 0.92,
        "comment": null,
        "name": 0.84,
        "unit": null,
        "quantity": 0.99,
        "food": 0.84
      },
      "ingredient": {
        "title": "",
        "note": "",
        "unit": {
          "name": "",
          "description": "",
          "extras": {},
          "fraction": true,
          "abbreviation": "",
          "useAbbreviation": false
        },
        "food": {
          "name": "shishito peppers",
          "description": "",
          "extras": {},
          "labelId": null
        },
        "disableAmount": false,
        "quantity": 12,
        "originalText": null,
        "referenceId": "cda1e158-789f-4e90-8a5a-a80fa1611c2d"
      }
    },
    {
      "input": "1 head radicchio, cut into wedges",
      "confidence": {
        "average": 0.9,
        "comment": 0.89,
        "name": 0.87,
        "unit": 0.85,
        "quantity": 0.99,
        "food": 0.87
      },
      "ingredient": {
        "title": "",
        "note": "cut into wedges",
        "unit": {
          "name": "head",
          "description": "",
          "extras": {},
          "fraction": true,
          "abbreviation": "",
          "useAbbreviation": false
        },
        "food": {
          "name": "radicchio",
          "description": "",
          "extras": {},
          "labelId": null
        },
        "disableAmount": false,
        "quantity": 1,
        "originalText": null,
        "referenceId": "bec84622-dd58-4ee6-a391-4c1be8afed25"
      }
    },
    {
      "input": "2 tablespoon olive oil",
      "confidence": {
        "average": 0.99,
        "comment": null,
        "name": 1,
        "unit": 0.97,
        "quantity": 1,
        "food": 1
      },
      "ingredient": {
        "title": "",
        "note": "",
        "unit": {
          "name": "tablespoon",
          "description": "",
          "extras": {},
          "fraction": true,
          "abbreviation": "",
          "useAbbreviation": false
        },
        "food": {
          "name": "olive oil",
          "description": "",
          "extras": {},
          "labelId": null
        },
        "disableAmount": false,
        "quantity": 2,
        "originalText": null,
        "referenceId": "23190a15-54f4-4798-b1ce-5008c2918102"
      }
    },
    {
      "input": "12 assorted sausage links",
      "confidence": {
        "average": 0.75,
        "comment": 0.6,
        "name": 0.66,
        "unit": null,
        "quantity": 0.99,
        "food": 0.66
      },
      "ingredient": {
        "title": "",
        "note": "assorted",
        "unit": {
          "name": "",
          "description": "",
          "extras": {},
          "fraction": true,
          "abbreviation": "",
          "useAbbreviation": false
        },
        "food": {
          "name": "sausage links",
          "description": "",
          "extras": {},
          "labelId": null
        },
        "disableAmount": false,
        "quantity": 12,
        "originalText": null,
        "referenceId": "7c8ad12d-d1bb-4cf3-ad35-9e5d9cf5d1e4"
      }
    },
    {
      "input": "1 salt and pepper, to taste",
      "confidence": {
        "average": 0.78,
        "comment": 0.56,
        "name": 0.88,
        "unit": null,
        "quantity": 0.91,
        "food": 0.88
      },
      "ingredient": {
        "title": "",
        "note": ", to taste",
        "unit": {
          "name": "",
          "description": "",
          "extras": {},
          "fraction": true,
          "abbreviation": "",
          "useAbbreviation": false
        },
        "food": {
          "name": "salt and pepper",
          "description": "",
          "extras": {},
          "labelId": null
        },
        "disableAmount": false,
        "quantity": 1,
        "originalText": null,
        "referenceId": "58eb7820-d3ec-4eaf-a67b-4401ea5a542f"
      }
    },
    {
      "input": "1 package skewers",
      "confidence": {
        "average": 0.92,
        "comment": null,
        "name": 0.95,
        "unit": 0.84,
        "quantity": 0.97,
        "food": 0.95
      },
      "ingredient": {
        "title": "",
        "note": "",
        "unit": {
          "name": "package",
          "description": "",
          "extras": {},
          "fraction": true,
          "abbreviation": "",
          "useAbbreviation": false
        },
        "food": {
          "name": "skewers",
          "description": "",
          "extras": {},
          "labelId": null
        },
        "disableAmount": false,
        "quantity": 1,
        "originalText": null,
        "referenceId": "6665adfe-e60e-4ab7-9988-53a17fd2dc02"
      }
    }
  ],
  "recipeInstructions": [
    {
      "id": null,
      "title": "",
      "text": "If using wooden skewers, soak for 30 minutes prior to cooking.",
      "ingredientReferences": []
    },
    {
      "id": null,
      "title": "",
      "text": "Light a grill and warm to medium heat. Thread each type of sausage onto skewers. Then, thread peppers and radicchio onto separate skewers. Brush all skewers with olive oil and season with salt and pepper.",
      "ingredientReferences": []
    },
    {
      "id": null,
      "title": "",
      "text": "Grill sausages until cooked through, about 10-20 minutes, depending on the type of sausage. Grill the peppers until tender and blistered in spots, about 10 minutes. Cook the radicchio about 2 minutes per side.",
      "ingredientReferences": []
    }
  ],
  "nutrition": {
    "calories": null,
    "fatContent": null,
    "proteinContent": null,
    "carbohydrateContent": null,
    "fiberContent": null,
    "sodiumContent": null,
    "sugarContent": null
  },
  "settings": {
    "public": true,
    "showNutrition": true,
    "showAssets": true
  },
  "assets": [],
  "notes": [],
  "extras": {},
  "isOcrRecipe": false,
  "comments": []
}

I get an HTTP 500 error with the following error in response:

{
  "detail": {
    "message": "Unknown Error",
    "error": true,
    "exception": "__init__() missing 1 required positional argument: 'group_id'"
  }
}

I looked at the console output from the API service and saw the error corresponded with the following log entries:

ERROR: 09-Jun-23 07:08:35   Unknown Error on recipe controller action
ERROR: 09-Jun-23 07:08:35   __init__() missing 1 required positional argument: 'group_id'
Traceback (most recent call last):
  File "/app/./mealie/db/models/_model_utils/helpers.py", line 42, in safe_call
    return func(**get_valid_call(func, dict_args))
TypeError: __init__() missing 1 required positional argument: 'group_id'
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
  File "/app/./mealie/routes/recipe/recipe_crud_routes.py", line 305, in update_one
    recipe = self.service.update_one(slug, data)
  File "/app/./mealie/services/recipe/recipe_service.py", line 204, in update_one
    new_data = self.repos.recipes.update(slug, update_data)
  File "/app/./mealie/repos/repository_generic.py", line 180, in update
    entry.update(session=self.session, **new_data)  # type: ignore
  File "/app/./mealie/db/models/_model_base.py", line 23, in update
    self.__init__(*args, **kwarg)
  File "<string>", line 6, in __init__
  File "/app/./mealie/db/models/recipe/api_extras.py", line 18, in wrapper
    return func(*args, extras=extras, **kwargs)
  File "/app/./mealie/db/models/_model_utils/auto_init.py", line 185, in wrapper
    instances = handle_many_to_many(session, get_attr, relation_cls, val)
  File "/app/./mealie/db/models/_model_utils/auto_init.py", line 73, in handle_many_to_many
    return handle_one_to_many_list(session, get_attr, relation_cls, all_elements)
  File "/app/./mealie/db/models/_model_utils/auto_init.py", line 99, in handle_one_to_many_list
    new_elems = [safe_call(relation_cls, elem, session=session) for elem in elems_to_create]
  File "/app/./mealie/db/models/_model_utils/auto_init.py", line 99, in <listcomp>
    new_elems = [safe_call(relation_cls, elem, session=session) for elem in elems_to_create]
  File "/app/./mealie/db/models/_model_utils/helpers.py", line 44, in safe_call
    return func(**dict_args)
TypeError: __init__() missing 1 required positional argument: 'group_id'
INFO:     172.21.0.3:55738 - "PUT /api/recipes/sausage-mixed-grill HTTP/1.1" 500 Internal Server Error

I am not experienced enough with Python to be able to further debug the issue. I would appreciate any assistance in getting this request to work, since it's 1 of over 200 recipes that I would like to import from Harris Teeter's recipe site (since that's our local grocery market)

You can see the entire script that I'm using (with some details obfuscated) here Once I have a working version, I will be happy to release a cleaner & more optimized version of the script to the community.

Deployment

Docker (Linux)

Deployment Details

Deployed using docker-compose via Portainer with the following versions:

mealie-frontend image: hkotel/mealie:frontend-v1.0.0beta-5 mealie-api image: hkotel/mealie:api-v1.0.0beta-5

michael-genson commented 1 year ago

Are you able to reproduce this on the demo? https://demo.mealie.io/ https://demo.mealie.io/docs

The demo uses the nightly version. We may have fixed this already, but nightly is currently far ahead of beta5 so it may not be in that release.

jaapio commented 1 year ago

I have the same issue, running the latest nightly version.

boc-the-git commented 1 year ago

Confirmed this still exists, running up to date mealie-next branch.

Did testing via swagger (URL/docs/).. created the recipe per these screenshots:

image image

Then did the PUT with the same request body as the OP, with only the groupId and userId changed:

image image

Got a 500 response where Swagger didn't reveal too much, but my docker logs did:

image

mealie-dev    | ERROR: 24-Oct-23 09:38:53       __init__() missing 1 required positional argument: 'group_id'
mealie-dev    | Traceback (most recent call last):
mealie-dev    |   File "/app/mealie/db/models/_model_utils/helpers.py", line 42, in safe_call
mealie-dev    |     return func(**get_valid_call(func, dict_args))
mealie-dev    | TypeError: __init__() missing 1 required positional argument: 'group_id'
mealie-dev    |
mealie-dev    | During handling of the above exception, another exception occurred:
mealie-dev    |
mealie-dev    | Traceback (most recent call last):
mealie-dev    |   File "/app/mealie/routes/recipe/recipe_crud_routes.py", line 339, in update_one
mealie-dev    |     recipe = self.service.update_one(slug, data)
mealie-dev    |   File "/app/mealie/services/recipe/recipe_service.py", line 342, in update_one
mealie-dev    |     new_data = self.repos.recipes.update(slug, update_data)
mealie-dev    |   File "/app/mealie/repos/repository_generic.py", line 208, in update
mealie-dev    |     entry.update(session=self.session, **new_data)
mealie-dev    |   File "/app/mealie/db/models/_model_base.py", line 24, in update
mealie-dev    |     self.__init__(*args, **kwarg)
mealie-dev    |   File "<string>", line 6, in __init__
mealie-dev    |   File "/app/mealie/db/models/recipe/api_extras.py", line 19, in wrapper
mealie-dev    |     return func(*args, extras=extras, **kwargs)
mealie-dev    |   File "/app/mealie/db/models/_model_utils/auto_init.py", line 187, in wrapper
mealie-dev    |     instances = handle_many_to_many(session, get_attr, relation_cls, val)
mealie-dev    |   File "/app/mealie/db/models/_model_utils/auto_init.py", line 73, in handle_many_to_many
mealie-dev    |     return handle_one_to_many_list(session, get_attr, relation_cls, all_elements)
mealie-dev    |   File "/app/mealie/db/models/_model_utils/auto_init.py", line 100, in handle_one_to_many_list
mealie-dev    |     new_elems = [safe_call(relation_cls, elem, session=session) for elem in elems_to_create]
mealie-dev    |   File "/app/mealie/db/models/_model_utils/auto_init.py", line 100, in <listcomp>
mealie-dev    |     new_elems = [safe_call(relation_cls, elem, session=session) for elem in elems_to_create]
mealie-dev    |   File "/app/mealie/db/models/_model_utils/helpers.py", line 44, in safe_call
mealie-dev    |     return func(**dict_args)
mealie-dev    | TypeError: __init__() missing 1 required positional argument: 'group_id'
mealie-dev    | INFO:     192.168.2.153:0 - "PUT /api/recipes/sausage-mixed-grill?group_id=7a267ccc-f55d-4328-a356-121bc2c0e1c2 HTTP/1.1" 500 Internal Server Error```
Dylancyclone commented 1 year ago

I also had this error on the latest nightly, and traced it back to the recipeCategory and tags fields.

I don't know the technical details (or how it relates to group_id in the error), but from what I can gather, the error occurs when trying to set one of those fields to anything other than a RecipeCategory/RecipeTag type object respectively. These are seen in the schema part of the docs: image image

For example, since the original query is trying to tag the recipe with tags such as "Dinner" and "Pork", those tags must already exist within Mealie and you must refer to them using it's full identity:

"tags": [
  {'id': '6becb67b-a3bd-49d8-81f5-5c770bff079c', 'name': "Dinner", 'slug': 'dinner'},
  ...
]

Just passing "Dinner" seems to throw the error, even if "Dinner" exists in Mealie.

A workaround I have found while working on a similar importer is

# Request tags from Mealie and cache them, this way we don't have to ask Mealie if a tag already exists each time
tag_cache = requests.get(base_url + "/api/organizers/tags?perPage=100", headers=headers).json()["items"]

# ...

def get_tag_by_name(name):
    # If the requested tag already exists in Mealie, use that
    cached = [tag for tag in tag_cache  if tag["name"] == name]
    if len(cached) > 0:
        return cached[0]
    # If it's not cached (i.e. doesn't exist yet), create it in Mealie and add it to the cache
    created_tag = requests.post(base_url + "/api/organizers/tags", json={"name": name}, headers=headers).json()
    tag_cache .append(created_tag)
    return created_tag

# ...

# Then when constructing the object that is sent to Mealie
tags = [get_tag_by_name(tag) for tag in tag_strings]

...and then basically duplicated for categories

It works, but is certainly less convenient than just passing Mealie the category/tag names

hay-kot commented 4 hours ago

Going to close this. Reviewing what @Dylancyclone had said and confirming with the API spec we do require that full objects for tags and categories so it is an issue with your payload.

I agree that it would be nice if Mealie handled this automatically, but it doesn't at the moment. If anyone want to look into a PR to handle this I'm sure it would be welcome!