sqlalchemy-history icon indicating copy to clipboard operation
sqlalchemy-history copied to clipboard

sqlalchemy v2.0.36 MappedAsDataclass

Open benedikt-bartscher opened this issue 1 year ago • 4 comments

sqlalchemy introduced a new check/validation which breaks sqlalchemy-history compability with MappedAsDataclass

from sqlalchemy.orm import DeclarativeBase, Mapped, MappedAsDataclass, mapped_column
from sqlalchemy_history import make_versioned

make_versioned(user_cls="")


class Base(MappedAsDataclass, DeclarativeBase):
    pass


class MyModel(Base):
    __versioned__ = {}
    __tablename__ = "my_model"
    id: Mapped[int] = mapped_column(primary_key=True)


_ = MyModel(id=1)
sqlalchemy.exc.InvalidRequestError: Class <class 'sqlalchemy_history.model_builder.MyModelVersion'> already defines a '__table__'. ORM Annotated Dataclasses do not support a pre-existing '__table__' element

upstream refs: https://github.com/sqlalchemy/sqlalchemy/issues/11973 https://github.com/sqlalchemy/sqlalchemy/commit/270b46cef3043d0e675ccb72b1e3a590f835dd4b

benedikt-bartscher avatar Oct 20 '24 13:10 benedikt-bartscher

Hm, I havent tried MappedAsDataclass yet - do you have any experience or suggestions on how to get this to work ? PRs are welcome !

AbdealiLoKo avatar Oct 25 '24 03:10 AbdealiLoKo

Yes, I can confirm this.

abulvenz avatar Oct 28 '24 13:10 abulvenz

Hm, I havent tried MappedAsDataclass yet - do you have any experience or suggestions on how to get this to work ? PRs are welcome !

Not really, i do not have looked into the sqlalchemy-history internals yet, but maybe i will in the next days

benedikt-bartscher avatar Oct 28 '24 13:10 benedikt-bartscher

The issue is that sqlalchemy-history tries to build a versioned class based on the model base class (the class inheriting from DeclarativeBase) while using __table__ for the fields. If your base class has MappedAsDataClass it won't accept __table__ with sqlalchemy 2.0.36.

You can mitigate it like this: before:

class Base(DeclarativeBase, MappedAsDataclass):
    pass

make_versioned(options={"base_classes": (Base)})

class YourModel(Base): ....

after:

class Base(DeclarativeBase):
    pass

class DataclassBase(Base, MappedAsDataclass):
    __abstract__ = True

make_versioned(options={"base_classes": (Base)})

class YourModel(DataclassBase): ....

benedikt-bartscher avatar Dec 05 '24 01:12 benedikt-bartscher