From: Sebastián Ramírez Date: Sat, 17 Feb 2024 13:34:57 +0000 (+0100) Subject: 🐛 Fix class initialization compatibility with Pydantic and SQLModel, fixing errors... X-Git-Tag: 0.0.15~2 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=1b7b3aa668cf3cd70bbaa2024a7a2eb015af9bc8;p=thirdparty%2Ffastapi%2Fsqlmodel.git 🐛 Fix class initialization compatibility with Pydantic and SQLModel, fixing errors revealed by the latest Pydantic (#807) --- diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index ade60f25..89da640d 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -62,10 +62,10 @@ jobs: run: python -m poetry install - name: Install Pydantic v1 if: matrix.pydantic-version == 'pydantic-v1' - run: pip install "pydantic>=1.10.0,<2.0.0" + run: pip install --upgrade "pydantic>=1.10.0,<2.0.0" - name: Install Pydantic v2 if: matrix.pydantic-version == 'pydantic-v2' - run: pip install "pydantic>=2.0.2,<3.0.0" + run: pip install --upgrade "pydantic>=2.0.2,<3.0.0" - name: Lint # Do not run on Python 3.7 as mypy behaves differently if: matrix.python-version != '3.7' && matrix.pydantic-version == 'pydantic-v2' diff --git a/sqlmodel/_compat.py b/sqlmodel/_compat.py index 2a2caca3..76771ce7 100644 --- a/sqlmodel/_compat.py +++ b/sqlmodel/_compat.py @@ -97,10 +97,10 @@ if IS_PYDANTIC_V2: def get_model_fields(model: InstanceOrType["SQLModel"]) -> Dict[str, "FieldInfo"]: return model.model_fields - def set_fields_set( - new_object: InstanceOrType["SQLModel"], fields: Set["FieldInfo"] - ) -> None: - object.__setattr__(new_object, "__pydantic_fields_set__", fields) + def init_pydantic_private_attrs(new_object: InstanceOrType["SQLModel"]) -> None: + object.__setattr__(new_object, "__pydantic_fields_set__", set()) + object.__setattr__(new_object, "__pydantic_extra__", None) + object.__setattr__(new_object, "__pydantic_private__", None) def get_annotations(class_dict: Dict[str, Any]) -> Dict[str, Any]: return class_dict.get("__annotations__", {}) @@ -387,10 +387,8 @@ else: def get_model_fields(model: InstanceOrType["SQLModel"]) -> Dict[str, "FieldInfo"]: return model.__fields__ # type: ignore - def set_fields_set( - new_object: InstanceOrType["SQLModel"], fields: Set["FieldInfo"] - ) -> None: - object.__setattr__(new_object, "__fields_set__", fields) + def init_pydantic_private_attrs(new_object: InstanceOrType["SQLModel"]) -> None: + object.__setattr__(new_object, "__fields_set__", set()) def get_annotations(class_dict: Dict[str, Any]) -> Dict[str, Any]: return resolve_annotations( # type: ignore[no-any-return] diff --git a/sqlmodel/main.py b/sqlmodel/main.py index 10064c71..fec3bc79 100644 --- a/sqlmodel/main.py +++ b/sqlmodel/main.py @@ -70,11 +70,11 @@ from ._compat import ( # type: ignore[attr-defined] get_model_fields, get_relationship_to, get_type_from_field, + init_pydantic_private_attrs, is_field_noneable, is_table_model_class, post_init_field_info, set_config_value, - set_fields_set, sqlmodel_init, sqlmodel_validate, ) @@ -686,12 +686,12 @@ class SQLModel(BaseModel, metaclass=SQLModelMetaclass, registry=default_registry def __new__(cls, *args: Any, **kwargs: Any) -> Any: new_object = super().__new__(cls) - # SQLAlchemy doesn't call __init__ on the base class + # SQLAlchemy doesn't call __init__ on the base class when querying from DB # Ref: https://docs.sqlalchemy.org/en/14/orm/constructors.html # Set __fields_set__ here, that would have been set when calling __init__ # in the Pydantic model so that when SQLAlchemy sets attributes that are # added (e.g. when querying from DB) to the __fields_set__, this already exists - set_fields_set(new_object, set()) + init_pydantic_private_attrs(new_object) return new_object def __init__(__pydantic_self__, **data: Any) -> None: