From ce2f28c37e0a2f2aa3b4a404ee190cdc00b8b918 Mon Sep 17 00:00:00 2001 From: Mike Bayer Date: Thu, 18 Mar 2021 15:07:03 -0400 Subject: Adjust dataclass rules to account for field w/ default Fixed issue in new ORM dataclasses functionality where dataclass fields on an abstract base or mixin that contained column or other mapping constructs would not be mapped if they also included a "default" key within the dataclasses.field() object. Fixes: #6093 Change-Id: I628086ceb48ab1dd0702f239cd12be74074f58f1 --- lib/sqlalchemy/orm/decl_base.py | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) (limited to 'lib/sqlalchemy') diff --git a/lib/sqlalchemy/orm/decl_base.py b/lib/sqlalchemy/orm/decl_base.py index a21af192e..0a73288fd 100644 --- a/lib/sqlalchemy/orm/decl_base.py +++ b/lib/sqlalchemy/orm/decl_base.py @@ -15,6 +15,7 @@ from sqlalchemy.orm import instrumentation from . import clsregistry from . import exc as orm_exc from . import mapper as mapperlib +from .attributes import InstrumentedAttribute from .attributes import QueryableAttribute from .base import _is_mapped_class from .base import InspectionAttr @@ -366,18 +367,24 @@ class _ClassScanMapperConfig(_MapperConfig): elif ret is not absent: return True + all_field = all_datacls_fields.get(key, absent) + ret = getattr(cls, key, obj) if ret is obj: return False - elif ret is not absent: - return True - ret = all_datacls_fields.get(key, absent) + # for dataclasses, this could be the + # 'default' of the field. so filter more specifically + # for an already-mapped InstrumentedAttribute + if ret is not absent and isinstance( + ret, InstrumentedAttribute + ): + return True - if ret is obj: + if all_field is obj: return False - elif ret is not absent: + elif all_field is not absent: return True # can't find another attribute @@ -401,15 +408,18 @@ class _ClassScanMapperConfig(_MapperConfig): yield name, obj else: + field_names = set() def local_attributes_for_class(): - for name, obj in vars(cls).items(): - yield name, obj for field in util.local_dataclass_fields(cls): if sa_dataclass_metadata_key in field.metadata: + field_names.add(field.name) yield field.name, field.metadata[ sa_dataclass_metadata_key ] + for name, obj in vars(cls).items(): + if name not in field_names: + yield name, obj return local_attributes_for_class -- cgit v1.2.1