summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Bayer <mike_mp@zzzcomputing.com>2020-09-27 11:44:58 -0400
committerMike Bayer <mike_mp@zzzcomputing.com>2020-09-27 11:45:49 -0400
commit17dd7fce6164794f6fd75a9351061f109e3360b1 (patch)
treeec85112b76a1124c113993d8124c7516134dfae5
parentffafbd4b9657a4ee8bec57c0861414144f37bdc5 (diff)
downloadsqlalchemy-17dd7fce6164794f6fd75a9351061f109e3360b1.tar.gz
Accommodate for same base class multiple times in inherits list
Improved declarative inheritance scanning to not get tripped up when the same base class appears multiple times in the base inheritance list. Fixes: #4699 Change-Id: I932e735cd2e2c1efa935936c84219924225d10f1
-rw-r--r--doc/build/changelog/unreleased_14/4699.rst7
-rw-r--r--lib/sqlalchemy/orm/decl_base.py3
-rw-r--r--test/orm/declarative/test_mixin.py25
3 files changed, 34 insertions, 1 deletions
diff --git a/doc/build/changelog/unreleased_14/4699.rst b/doc/build/changelog/unreleased_14/4699.rst
new file mode 100644
index 000000000..e1e1442cf
--- /dev/null
+++ b/doc/build/changelog/unreleased_14/4699.rst
@@ -0,0 +1,7 @@
+.. change::
+ :tags: bug, orm
+ :tickets: 4699
+
+ Improved declarative inheritance scanning to not get tripped up when the
+ same base class appears multiple times in the base inheritance list.
+
diff --git a/lib/sqlalchemy/orm/decl_base.py b/lib/sqlalchemy/orm/decl_base.py
index b9c890429..644e4aff6 100644
--- a/lib/sqlalchemy/orm/decl_base.py
+++ b/lib/sqlalchemy/orm/decl_base.py
@@ -677,7 +677,8 @@ class _ClassScanMapperConfig(_MapperConfig):
) is not None and not _get_immediate_cls_attr(
c, "_sa_decl_prepare_nocascade", strict=True
):
- inherits_search.append(c)
+ if c not in inherits_search:
+ inherits_search.append(c)
if inherits_search:
if len(inherits_search) > 1:
diff --git a/test/orm/declarative/test_mixin.py b/test/orm/declarative/test_mixin.py
index eed918572..bc36ee962 100644
--- a/test/orm/declarative/test_mixin.py
+++ b/test/orm/declarative/test_mixin.py
@@ -218,6 +218,31 @@ class DeclarativeMixinTest(DeclarativeTestBase):
eq_(MyModelA.__table__.c.foo.type.__class__, String)
eq_(MyModelB.__table__.c.foo.type.__class__, Integer)
+ def test_same_base_multiple_times(self):
+ class User(Base):
+ __tablename__ = "user"
+
+ id = Column(Integer, primary_key=True)
+ name = Column(String)
+ surname = Column(String)
+
+ class SpecialUser(User):
+ __abstract__ = True
+
+ class ConvenienceStuff(User):
+ __abstract__ = True
+
+ def fullname(self):
+ return self.name + " " + self.surname
+
+ class Manager(SpecialUser, ConvenienceStuff, User):
+ __tablename__ = "manager"
+
+ id = Column(Integer, ForeignKey("user.id"), primary_key=True)
+ title = Column(String)
+
+ eq_(Manager.__table__.name, "manager")
+
def test_not_allowed(self):
class MyMixin:
foo = Column(Integer, ForeignKey("bar.id"))