summaryrefslogtreecommitdiff
path: root/lib/sqlalchemy/orm
diff options
context:
space:
mode:
authorMike Bayer <mike_mp@zzzcomputing.com>2022-10-26 13:27:21 -0400
committerMike Bayer <mike_mp@zzzcomputing.com>2022-10-26 14:31:28 -0400
commit73be84ae46473703dcf7b8d39e9666496fb07c8f (patch)
tree28740ff53f1efc30c5273d867f5e377f40a69390 /lib/sqlalchemy/orm
parent811979150cd9f1aed3d6de6938b84179b2092b89 (diff)
downloadsqlalchemy-73be84ae46473703dcf7b8d39e9666496fb07c8f.tar.gz
ensure inherited mapper attrs not interpreted as plain dataclass fields
Fixed issue in new dataclass mapping feature where a column declared on the decalrative base / abstract base / mixin would leak into the constructor for an inheriting subclass under some circumstances. Fixes: #8718 Change-Id: Ic519acf239e2f80541516f10995991cbbbed00bd
Diffstat (limited to 'lib/sqlalchemy/orm')
-rw-r--r--lib/sqlalchemy/orm/decl_base.py23
-rw-r--r--lib/sqlalchemy/orm/interfaces.py20
2 files changed, 33 insertions, 10 deletions
diff --git a/lib/sqlalchemy/orm/decl_base.py b/lib/sqlalchemy/orm/decl_base.py
index 4e79ecc6f..4e02e589b 100644
--- a/lib/sqlalchemy/orm/decl_base.py
+++ b/lib/sqlalchemy/orm/decl_base.py
@@ -772,7 +772,6 @@ class _ClassScanMapperConfig(_MapperConfig):
annotation,
is_dataclass_field,
) in local_attributes_for_class():
-
if re.match(r"^__.+__$", name):
if name == "__mapper_args__":
check_decl = _check_declared_props_nocascade(
@@ -825,6 +824,7 @@ class _ClassScanMapperConfig(_MapperConfig):
"not applying to subclass %s."
% (base.__name__, name, base, cls)
)
+
continue
elif base is not cls:
# we're a mixin, abstract base, or something that is
@@ -990,10 +990,15 @@ class _ClassScanMapperConfig(_MapperConfig):
_AttributeOptions._get_arguments_for_make_dataclass(
key,
anno,
+ mapped_container,
self.collected_attributes.get(key, _NoArg.NO_ARG),
)
- for key, anno in (
- (key, mapped_anno if mapped_anno else raw_anno)
+ for key, anno, mapped_container in (
+ (
+ key,
+ mapped_anno if mapped_anno else raw_anno,
+ mapped_container,
+ )
for key, (
raw_anno,
mapped_container,
@@ -1003,7 +1008,6 @@ class _ClassScanMapperConfig(_MapperConfig):
) in self.collected_annotations.items()
)
]
-
annotations = {}
defaults = {}
for item in field_list:
@@ -1139,7 +1143,6 @@ class _ClassScanMapperConfig(_MapperConfig):
# copy mixin columns to the mapped class
for name, obj, annotation, is_dataclass in attributes_for_class():
-
if (
not fixed_table
and obj is None
@@ -1154,14 +1157,16 @@ class _ClassScanMapperConfig(_MapperConfig):
elif isinstance(obj, (Column, MappedColumn)):
- obj = self._collect_annotation(name, annotation, True, obj)
-
if attribute_is_overridden(name, obj):
# if column has been overridden
# (like by the InstrumentedAttribute of the
- # superclass), skip
+ # superclass), skip. don't collect the annotation
+ # either (issue #8718)
continue
- elif name not in dict_ and not (
+
+ obj = self._collect_annotation(name, annotation, True, obj)
+
+ if name not in dict_ and not (
"__table__" in dict_
and (getattr(obj, "name", None) or name)
in dict_["__table__"].c
diff --git a/lib/sqlalchemy/orm/interfaces.py b/lib/sqlalchemy/orm/interfaces.py
index 9903c5f4a..1747bfd9b 100644
--- a/lib/sqlalchemy/orm/interfaces.py
+++ b/lib/sqlalchemy/orm/interfaces.py
@@ -213,7 +213,11 @@ class _AttributeOptions(NamedTuple):
@classmethod
def _get_arguments_for_make_dataclass(
- cls, key: str, annotation: Type[Any], elem: _T
+ cls,
+ key: str,
+ annotation: Type[Any],
+ mapped_container: Optional[Any],
+ elem: _T,
) -> Union[
Tuple[str, Type[Any]], Tuple[str, Type[Any], dataclasses.Field[Any]]
]:
@@ -229,7 +233,21 @@ class _AttributeOptions(NamedTuple):
elif elem is not _NoArg.NO_ARG:
# why is typing not erroring on this?
return (key, annotation, elem)
+ elif mapped_container is not None:
+ # it's Mapped[], but there's no "element", which means declarative
+ # did not actually do anything for this field. this shouldn't
+ # happen.
+ # previously, this would occur because _scan_attributes would
+ # skip a field that's on an already mapped superclass, but it
+ # would still include it in the annotations, leading
+ # to issue #8718
+
+ assert False, "Mapped[] received without a mapping declaration"
+
else:
+ # plain dataclass field, not mapped. Is only possible
+ # if __allow_unmapped__ is set up. I can see this mode causing
+ # problems...
return (key, annotation)