fix: Pydantic Serialization Issues (#3157)

* replaced pydantic inits with validators

* fixed serialization dropping food and unit ids
This commit is contained in:
Michael Genson 2024-02-11 17:34:56 -06:00 committed by GitHub
parent 67313f8f03
commit df75cb4034
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 16 additions and 20 deletions

View File

@ -2,7 +2,7 @@ from __future__ import annotations
from datetime import datetime from datetime import datetime
from pydantic import UUID4, ConfigDict, field_validator from pydantic import UUID4, ConfigDict, field_validator, model_validator
from sqlalchemy.orm import joinedload, selectinload from sqlalchemy.orm import joinedload, selectinload
from sqlalchemy.orm.interfaces import LoaderOption from sqlalchemy.orm.interfaces import LoaderOption
@ -100,14 +100,15 @@ class ShoppingListItemOut(ShoppingListItemBase):
created_at: datetime | None = None created_at: datetime | None = None
update_at: datetime | None = None update_at: datetime | None = None
def __init__(self, **kwargs): @model_validator(mode="after")
super().__init__(**kwargs) def post_validate(self):
# if we're missing a label, but the food has a label, use that as the label # if we're missing a label, but the food has a label, use that as the label
if (not self.label) and (self.food and self.food.label): if (not self.label) and (self.food and self.food.label):
self.label = self.food.label self.label = self.food.label
self.label_id = self.label.id self.label_id = self.label.id
return self
model_config = ConfigDict(from_attributes=True) model_config = ConfigDict(from_attributes=True)
@classmethod @classmethod

View File

@ -6,7 +6,7 @@ from pathlib import Path
from typing import Annotated, Any, ClassVar from typing import Annotated, Any, ClassVar
from uuid import uuid4 from uuid import uuid4
from pydantic import UUID4, BaseModel, ConfigDict, Field, field_validator from pydantic import UUID4, BaseModel, ConfigDict, Field, field_validator, model_validator
from pydantic_core.core_schema import ValidationInfo from pydantic_core.core_schema import ValidationInfo
from slugify import slugify from slugify import slugify
from sqlalchemy import Select, desc, func, or_, select, text from sqlalchemy import Select, desc, func, or_, select, text
@ -183,17 +183,8 @@ class Recipe(RecipeSummary):
model_config = ConfigDict(from_attributes=True) model_config = ConfigDict(from_attributes=True)
@classmethod @model_validator(mode="after")
def model_validate(cls, obj): def post_validate(self):
recipe = super().model_validate(obj)
recipe.__post_init__()
return recipe
def __init__(self, **kwargs) -> None:
super().__init__(**kwargs)
self.__post_init__()
def __post_init__(self) -> None:
# the ingredient disable_amount property is unreliable, # the ingredient disable_amount property is unreliable,
# so we set it here and recalculate the display property # so we set it here and recalculate the display property
disable_amount = self.settings.disable_amount if self.settings else True disable_amount = self.settings.disable_amount if self.settings else True
@ -202,6 +193,8 @@ class Recipe(RecipeSummary):
ingredient.is_food = not ingredient.disable_amount ingredient.is_food = not ingredient.disable_amount
ingredient.display = ingredient._format_display() ingredient.display = ingredient._format_display()
return self
@field_validator("slug", mode="before") @field_validator("slug", mode="before")
def validate_slug(slug: str, info: ValidationInfo): def validate_slug(slug: str, info: ValidationInfo):
if not info.data.get("name"): if not info.data.get("name"):

View File

@ -6,7 +6,7 @@ from fractions import Fraction
from typing import ClassVar from typing import ClassVar
from uuid import UUID, uuid4 from uuid import UUID, uuid4
from pydantic import UUID4, ConfigDict, Field, field_validator from pydantic import UUID4, ConfigDict, Field, field_validator, model_validator
from sqlalchemy.orm import joinedload from sqlalchemy.orm import joinedload
from sqlalchemy.orm.interfaces import LoaderOption from sqlalchemy.orm.interfaces import LoaderOption
@ -31,6 +31,7 @@ def display_fraction(fraction: Fraction):
class UnitFoodBase(MealieModel): class UnitFoodBase(MealieModel):
id: UUID4 | None = None
name: str name: str
plural_name: str | None = None plural_name: str | None = None
description: str = "" description: str = ""
@ -134,9 +135,8 @@ class RecipeIngredientBase(MealieModel):
Automatically calculated after the object is created, unless overwritten Automatically calculated after the object is created, unless overwritten
""" """
def __init__(self, **kwargs) -> None: @model_validator(mode="after")
super().__init__(**kwargs) def post_validate(self):
# calculate missing is_food and disable_amount values # calculate missing is_food and disable_amount values
# we can't do this in a validator since they depend on each other # we can't do this in a validator since they depend on each other
if self.is_food is None and self.disable_amount is not None: if self.is_food is None and self.disable_amount is not None:
@ -151,6 +151,8 @@ class RecipeIngredientBase(MealieModel):
if not self.display: if not self.display:
self.display = self._format_display() self.display = self._format_display()
return self
@field_validator("unit", mode="before") @field_validator("unit", mode="before")
@classmethod @classmethod
def validate_unit(cls, v): def validate_unit(cls, v):