diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index ade60f255..89da640d1 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/docs/release-notes.md b/docs/release-notes.md index 96c6feaf8..a0431a9bd 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -2,6 +2,12 @@ ## Latest Changes +## 0.0.15 + +### Fixes + +* 🐛 Fix class initialization compatibility with Pydantic and SQLModel, fixing errors revealed by the latest Pydantic. PR [#807](https://github.com/tiangolo/sqlmodel/pull/807) by [@tiangolo](https://github.com/tiangolo). + ### Internal * ⬆ Bump tiangolo/issue-manager from 0.4.0 to 0.4.1. PR [#775](https://github.com/tiangolo/sqlmodel/pull/775) by [@dependabot[bot]](https://github.com/apps/dependabot). diff --git a/sqlmodel/__init__.py b/sqlmodel/__init__.py index 2df3afb51..c9629a98b 100644 --- a/sqlmodel/__init__.py +++ b/sqlmodel/__init__.py @@ -1,4 +1,4 @@ -__version__ = "0.0.14" +__version__ = "0.0.15" # Re-export from SQLAlchemy from sqlalchemy.engine import create_engine as create_engine diff --git a/sqlmodel/_compat.py b/sqlmodel/_compat.py index 9e79f6b1a..072d2b0f5 100644 --- a/sqlmodel/_compat.py +++ b/sqlmodel/_compat.py @@ -106,10 +106,10 @@ def get_fields_set( ) -> Union[Set[str], Callable[[BaseModel], Set[str]]]: return object.model_fields_set - 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__", {}) @@ -401,10 +401,8 @@ def get_fields_set( ) -> Union[Set[str], Callable[[BaseModel], Set[str]]]: return object.__fields_set__ - 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 161464979..9e8330d69 100644 --- a/sqlmodel/main.py +++ b/sqlmodel/main.py @@ -70,11 +70,11 @@ 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 Config: 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: