🐛 Fix class initialization compatibility with Pydantic and SQLModel, fixing errors revealed by the latest Pydantic (#807)

This commit is contained in:
Sebastián Ramírez 2024-02-17 14:34:57 +01:00 committed by GitHub
parent 0c7def88b5
commit 1b7b3aa668
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 11 additions and 13 deletions

View File

@ -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'

View File

@ -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]

View File

@ -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: