Upgrade SQLAlchemy to 2.0, including initial work by farahats9 (#700)

Co-authored-by: Mohamed Farahat <farahats9@yahoo.com>
Co-authored-by: Stefan Borer <stefan.borer@gmail.com>
Co-authored-by: Peter Landry <peter.landry@gmail.com>
This commit is contained in:
Sebastián Ramírez
2023-11-18 12:30:37 +01:00
committed by GitHub
parent 77c6fed305
commit 8ed856d322
24 changed files with 808 additions and 510 deletions

View File

@@ -45,12 +45,19 @@ from sqlalchemy import (
inspect,
)
from sqlalchemy import Enum as sa_Enum
from sqlalchemy.orm import RelationshipProperty, declared_attr, registry, relationship
from sqlalchemy.orm import (
Mapped,
RelationshipProperty,
declared_attr,
registry,
relationship,
)
from sqlalchemy.orm.attributes import set_attribute
from sqlalchemy.orm.decl_api import DeclarativeMeta
from sqlalchemy.orm.instrumentation import is_instrumented
from sqlalchemy.sql.schema import MetaData
from sqlalchemy.sql.sqltypes import LargeBinary, Time
from typing_extensions import get_origin
from .sql.sqltypes import GUID, AutoString
@@ -483,7 +490,16 @@ class SQLModelMetaclass(ModelMetaclass, DeclarativeMeta):
# over anything else, use that and continue with the next attribute
setattr(cls, rel_name, rel_info.sa_relationship) # Fix #315
continue
ann = cls.__annotations__[rel_name]
raw_ann = cls.__annotations__[rel_name]
origin = get_origin(raw_ann)
if origin is Mapped:
ann = raw_ann.__args__[0]
else:
ann = raw_ann
# Plain forward references, for models not yet defined, are not
# handled well by SQLAlchemy without Mapped, so, wrap the
# annotations in Mapped here
cls.__annotations__[rel_name] = Mapped[ann] # type: ignore[valid-type]
temp_field = ModelField.infer(
name=rel_name,
value=rel_info,
@@ -511,9 +527,7 @@ class SQLModelMetaclass(ModelMetaclass, DeclarativeMeta):
rel_args.extend(rel_info.sa_relationship_args)
if rel_info.sa_relationship_kwargs:
rel_kwargs.update(rel_info.sa_relationship_kwargs)
rel_value: RelationshipProperty = relationship( # type: ignore
relationship_to, *rel_args, **rel_kwargs
)
rel_value = relationship(relationship_to, *rel_args, **rel_kwargs)
setattr(cls, rel_name, rel_value) # Fix #315
# SQLAlchemy no longer uses dict_
# Ref: https://github.com/sqlalchemy/sqlalchemy/commit/428ea01f00a9cc7f85e435018565eb6da7af1b77
@@ -642,6 +656,7 @@ class SQLModel(BaseModel, metaclass=SQLModelMetaclass, registry=default_registry
__sqlmodel_relationships__: ClassVar[Dict[str, RelationshipProperty]] # type: ignore
__name__: ClassVar[str]
metadata: ClassVar[MetaData]
__allow_unmapped__ = True # https://docs.sqlalchemy.org/en/20/changelog/migration_20.html#migration-20-step-six
class Config:
orm_mode = True
@@ -685,7 +700,7 @@ class SQLModel(BaseModel, metaclass=SQLModelMetaclass, registry=default_registry
return
else:
# Set in SQLAlchemy, before Pydantic to trigger events and updates
if getattr(self.__config__, "table", False) and is_instrumented(self, name):
if getattr(self.__config__, "table", False) and is_instrumented(self, name): # type: ignore
set_attribute(self, name, value)
# Set in Pydantic model to trigger possible validation changes, only for
# non relationship values