support multiple AbstractConcreteBase branch points in a hierarchy

Issue #3484 new
Mike Bayer repo owner created an issue

e.g. below, we can query Document or ContactDocument and get a polymorphic query:

class Document(AbstractConcreteBase, Base):
    date = Column(Date)
    documentType = Column(String)


class NotAContact(Document):
    __tablename__ = 'not_a_contact'

    id = Column(Integer, primary_key=True)

    __mapper_args__ = {'polymorphic_identity': 'nc'}


class ABCMiddle(object):
    _sa_abc_base = True

    @classmethod
    def _sa_decl_prepare_nocascade(cls):
        AbstractConcreteBase._sa_decl_prepare_nocascade.__func__(cls)


class ContactDocument(ABCMiddle, Document):

    contactPersonName = Column(String)
    salesPersonName = Column(String)
    sendMethod = Column(String)

    @declared_attr
    def company_id(self):
        return Column(ForeignKey('companies.id'))


class Offer(ContactDocument):
    __tablename__ = 'offers'

    id = Column(Integer, primary_key=True)

    __mapper_args__ = {'polymorphic_identity': 'offer'}

there's an easy patch that seems to do this, verify with tests and we can add to 1.0 with "experimental" label

diff --git a/lib/sqlalchemy/ext/declarative/api.py b/lib/sqlalchemy/ext/declarative/api.py
index 3d46bd4..7493df3 100644
--- a/lib/sqlalchemy/ext/declarative/api.py
+++ b/lib/sqlalchemy/ext/declarative/api.py
@@ -20,7 +20,7 @@ import weakref

 from .base import _as_declarative, \
     _declarative_constructor,\
-    _DeferredMapperConfig, _add_attribute
+    _DeferredMapperConfig, _add_attribute, _get_immediate_cls_attr
 from .clsregistry import _class_resolver


@@ -506,7 +506,8 @@ class AbstractConcreteBase(ConcreteBase):

     @classmethod
     def _sa_decl_prepare_nocascade(cls):
-        if getattr(cls, '__mapper__', None):
+        if getattr(cls, '__mapper__', None) and \
+                not _get_immediate_cls_attr(cls, '_sa_abc_base', strict=True):
             return

         to_map = _DeferredMapperConfig.config_for_cls(cls)

then the middleware is:

class ABCMiddle(object):
    _sa_abc_base = True

    @classmethod
    def _sa_decl_prepare_nocascade(cls):
        AbstractConcreteBase._sa_decl_prepare_nocascade.__func__(cls)

Comments (2)

  1. Log in to comment