feat: Upgrade to Pydantic V2 (#3134)

* bumped pydantic
This commit is contained in:
Michael Genson
2024-02-11 10:47:37 -06:00
committed by GitHub
parent 248459671e
commit 7a107584c7
129 changed files with 1138 additions and 833 deletions

View File

@@ -1,7 +1,7 @@
from functools import wraps
from uuid import UUID
from pydantic import BaseModel, Field, NoneStr
from pydantic import BaseModel, ConfigDict, Field
from sqlalchemy import select
from sqlalchemy.orm import MANYTOMANY, MANYTOONE, ONETOMANY, Session
from sqlalchemy.orm.mapper import Mapper
@@ -21,7 +21,7 @@ class AutoInitConfig(BaseModel):
Config class for `auto_init` decorator.
"""
get_attr: NoneStr = None
get_attr: str | None = None
exclude: set = Field(default_factory=_default_exclusion)
# auto_create: bool = False
@@ -31,16 +31,16 @@ def _get_config(relation_cls: type[SqlAlchemyBase]) -> AutoInitConfig:
Returns the config for the given class.
"""
cfg = AutoInitConfig()
cfgKeys = cfg.dict().keys()
cfgKeys = cfg.model_dump().keys()
# Get the config for the class
try:
class_config: AutoInitConfig = relation_cls.Config
class_config: ConfigDict = relation_cls.model_config
except AttributeError:
return cfg
# Map all matching attributes in Config to all AutoInitConfig attributes
for attr in dir(class_config):
for attr in class_config:
if attr in cfgKeys:
setattr(cfg, attr, getattr(class_config, attr))
setattr(cfg, attr, class_config[attr])
return cfg
@@ -97,7 +97,7 @@ def handle_one_to_many_list(
updated_elems.append(existing_elem)
new_elems = [safe_call(relation_cls, elem, session=session) for elem in elems_to_create]
new_elems = [safe_call(relation_cls, elem.copy(), session=session) for elem in elems_to_create]
return new_elems + updated_elems
@@ -164,7 +164,7 @@ def auto_init(): # sourcery no-metrics
setattr(self, key, instances)
elif relation_dir == ONETOMANY:
instance = safe_call(relation_cls, val, session=session)
instance = safe_call(relation_cls, val.copy() if val else None, session=session)
setattr(self, key, instance)
elif relation_dir == MANYTOONE and not use_list:

View File

@@ -29,12 +29,15 @@ def get_valid_call(func: Callable, args_dict) -> dict:
return {k: v for k, v in args_dict.items() if k in valid_args}
def safe_call(func, dict_args: dict, **kwargs) -> Any:
def safe_call(func, dict_args: dict | None, **kwargs) -> Any:
"""
Safely calls the supplied function with the supplied dictionary of arguments.
by removing any invalid arguments.
"""
if dict_args is None:
dict_args = {}
if kwargs:
dict_args.update(kwargs)

View File

@@ -2,6 +2,7 @@ from typing import TYPE_CHECKING, Optional
import sqlalchemy as sa
import sqlalchemy.orm as orm
from pydantic import ConfigDict
from sqlalchemy import select
from sqlalchemy.orm import Mapped, mapped_column
from sqlalchemy.orm.session import Session
@@ -79,9 +80,8 @@ class Group(SqlAlchemyBase, BaseMixins):
ingredient_foods: Mapped[list["IngredientFoodModel"]] = orm.relationship("IngredientFoodModel", **common_args)
tools: Mapped[list["Tool"]] = orm.relationship("Tool", **common_args)
tags: Mapped[list["Tag"]] = orm.relationship("Tag", **common_args)
class Config:
exclude = {
model_config = ConfigDict(
exclude={
"users",
"webhooks",
"shopping_lists",
@@ -91,6 +91,7 @@ class Group(SqlAlchemyBase, BaseMixins):
"mealplans",
"data_exports",
}
)
@auto_init()
def __init__(self, **_) -> None:

View File

@@ -1,6 +1,7 @@
from datetime import datetime
from typing import TYPE_CHECKING
from pydantic import ConfigDict
from sqlalchemy import ForeignKey, orm
from sqlalchemy.orm import Mapped, mapped_column
from sqlalchemy.sql.sqltypes import Boolean, DateTime, String
@@ -47,9 +48,7 @@ class ReportModel(SqlAlchemyBase, BaseMixins):
# Relationships
group_id: Mapped[GUID] = mapped_column(GUID, ForeignKey("groups.id"), nullable=False, index=True)
group: Mapped["Group"] = orm.relationship("Group", back_populates="group_reports", single_parent=True)
class Config:
exclude = ["entries"]
model_config = ConfigDict(exclude=["entries"])
@auto_init()
def __init__(self, **_) -> None:

View File

@@ -1,5 +1,6 @@
from typing import TYPE_CHECKING, Optional
from pydantic import ConfigDict
from sqlalchemy import Boolean, Float, ForeignKey, Integer, String, UniqueConstraint, orm
from sqlalchemy.ext.orderinglist import ordering_list
from sqlalchemy.orm import Mapped, mapped_column
@@ -69,9 +70,7 @@ class ShoppingListItem(SqlAlchemyBase, BaseMixins):
recipe_references: Mapped[list[ShoppingListItemRecipeReference]] = orm.relationship(
ShoppingListItemRecipeReference, cascade="all, delete, delete-orphan"
)
class Config:
exclude = {"id", "label", "food", "unit"}
model_config = ConfigDict(exclude={"id", "label", "food", "unit"})
@api_extras
@auto_init()
@@ -91,9 +90,7 @@ class ShoppingListRecipeReference(BaseMixins, SqlAlchemyBase):
)
recipe_quantity: Mapped[float] = mapped_column(Float, nullable=False)
class Config:
exclude = {"id", "recipe"}
model_config = ConfigDict(exclude={"id", "recipe"})
@auto_init()
def __init__(self, **_) -> None:
@@ -112,9 +109,7 @@ class ShoppingListMultiPurposeLabel(SqlAlchemyBase, BaseMixins):
"MultiPurposeLabel", back_populates="shopping_lists_label_settings"
)
position: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
class Config:
exclude = {"label"}
model_config = ConfigDict(exclude={"label"})
@auto_init()
def __init__(self, **_) -> None:
@@ -146,9 +141,7 @@ class ShoppingList(SqlAlchemyBase, BaseMixins):
collection_class=ordering_list("position"),
)
extras: Mapped[list[ShoppingListExtras]] = orm.relationship("ShoppingListExtras", cascade="all, delete-orphan")
class Config:
exclude = {"id", "list_items"}
model_config = ConfigDict(exclude={"id", "list_items"})
@api_extras
@auto_init()

View File

@@ -1,3 +1,4 @@
from pydantic import ConfigDict
from sqlalchemy import ForeignKey, Integer, String, orm
from sqlalchemy.orm import Mapped, mapped_column
@@ -28,12 +29,12 @@ class RecipeInstruction(SqlAlchemyBase):
ingredient_references: Mapped[list[RecipeIngredientRefLink]] = orm.relationship(
RecipeIngredientRefLink, cascade="all, delete-orphan"
)
class Config:
exclude = {
model_config = ConfigDict(
exclude={
"id",
"ingredient_references",
}
)
@auto_init()
def __init__(self, ingredient_references, session, **_) -> None:

View File

@@ -3,6 +3,7 @@ from typing import TYPE_CHECKING
import sqlalchemy as sa
import sqlalchemy.orm as orm
from pydantic import ConfigDict
from sqlalchemy import event
from sqlalchemy.ext.orderinglist import ordering_list
from sqlalchemy.orm import Mapped, mapped_column, validates
@@ -134,10 +135,9 @@ class RecipeModel(SqlAlchemyBase, BaseMixins):
# Automatically updated by sqlalchemy event, do not write to this manually
name_normalized: Mapped[str] = mapped_column(sa.String, nullable=False, index=True)
description_normalized: Mapped[str | None] = mapped_column(sa.String, index=True)
class Config:
get_attr = "slug"
exclude = {
model_config = ConfigDict(
get_attr="slug",
exclude={
"assets",
"notes",
"nutrition",
@@ -146,7 +146,8 @@ class RecipeModel(SqlAlchemyBase, BaseMixins):
"settings",
"comments",
"timeline_events",
}
},
)
@validates("name")
def validate_name(self, _, name):

View File

@@ -2,6 +2,7 @@ import enum
from datetime import datetime
from typing import TYPE_CHECKING, Optional
from pydantic import ConfigDict
from sqlalchemy import Boolean, DateTime, Enum, ForeignKey, Integer, String, orm
from sqlalchemy.ext.hybrid import hybrid_property
from sqlalchemy.orm import Mapped, mapped_column
@@ -84,9 +85,8 @@ class User(SqlAlchemyBase, BaseMixins):
favorite_recipes: Mapped[list["RecipeModel"]] = orm.relationship(
"RecipeModel", secondary=users_to_favorites, back_populates="favorited_by"
)
class Config:
exclude = {
model_config = ConfigDict(
exclude={
"password",
"admin",
"can_manage",
@@ -94,6 +94,7 @@ class User(SqlAlchemyBase, BaseMixins):
"can_organize",
"group",
}
)
@hybrid_property
def group_slug(self) -> str: