From: Mike Bayer Date: Sun, 27 Sep 2020 15:44:58 +0000 (-0400) Subject: Accommodate for same base class multiple times in inherits list X-Git-Tag: rel_1_4_0b1~73^2 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=17dd7fce6164794f6fd75a9351061f109e3360b1;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git 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 --- diff --git a/doc/build/changelog/unreleased_14/4699.rst b/doc/build/changelog/unreleased_14/4699.rst new file mode 100644 index 0000000000..e1e1442cf2 --- /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 b9c890429e..644e4aff63 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 eed9185728..bc36ee9624 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"))