A change to SQLAlchemy 2.1 dataclass handling made it incompatible with Flask-SQLAlchemy. This simple code snippet is enough to reproduce the problem:
from flask_sqlalchemy import SQLAlchemy
from sqlalchemy.orm import DeclarativeBase, MappedAsDataclass
class BaseModel(DeclarativeBase, MappedAsDataclass):
pass
db = SQLAlchemy(model_class=BaseModel)
# sqlalchemy.exc.InvalidRequestError: Class <class '__main__.FlaskSQLAlchemyBase'> is already a dataclass; ensure that base classes / decorator styles of establishing dataclasses are not being mixed. This can happen if a class that inherits from 'MappedAsDataclass', even indirectly, is been mapped with '@registry.mapped_as_dataclass'
Flask-SQLAlchemy uses types.new_class() to re-create user's model_class with added mixins. Because it also copies all attributes from the class namespace, the newly created class already has some attributes (specifically __dataclass_fields__) that MappedAsDataclass tries to add later when its __init_subclass__() is executed again. Since these attributes already exist, SQLAlchemy throws an error.
|
elif len(declarative_bases) == 1: |
|
body = dict(model_class.__dict__) |
|
body["__fsa__"] = self |
|
mixin_classes = [BindMixin, NameMixin, Model] |
|
if disable_autonaming: |
|
mixin_classes.remove(NameMixin) |
|
model = types.new_class( |
|
"FlaskSQLAlchemyBase", |
|
(*mixin_classes, *model_class.__bases__), |
|
{"metaclass": type(declarative_bases[0])}, |
|
lambda ns: ns.update(body), |
|
) |
I asked about it in the SQLAlchemy repository and it is not a bug in SQLAlchemy (sqlalchemy/sqlalchemy#13160).
Environment:
- Python version: 3.14.3
- Flask-SQLAlchemy version: 3.1.1
- SQLAlchemy version: 2.1.0b1
A change to SQLAlchemy 2.1 dataclass handling made it incompatible with Flask-SQLAlchemy. This simple code snippet is enough to reproduce the problem:
Flask-SQLAlchemy uses
types.new_class()to re-create user'smodel_classwith added mixins. Because it also copies all attributes from the class namespace, the newly created class already has some attributes (specifically__dataclass_fields__) thatMappedAsDataclasstries to add later when its__init_subclass__()is executed again. Since these attributes already exist, SQLAlchemy throws an error.flask-sqlalchemy/src/flask_sqlalchemy/extension.py
Lines 544 to 555 in 168cb4b
I asked about it in the SQLAlchemy repository and it is not a bug in SQLAlchemy (sqlalchemy/sqlalchemy#13160).
Environment: