Thanks for the response! The solution looks too hairy for my tastes, and I can manage with what I currently have. Good to know it's possible though!

10.07.2015, 02:25, Mike Bayer kirjoitti:


On 7/9/15 12:17 PM, Alex Grönholm wrote:
Thanks. What about my other question? Is it possible to have two layers of classes (Document and ContactDocument) mapped to polymorphic unions?
OK. So, AbstractConcreteBase struggles hard against Declarative wanting to map things. So as far as how to get it to take effect multiple times in a hierarchy, with ABC itself it would require more weird class tricks, of the kind where we always have to see, "does class A declare "_x" or is it inheriting it?" which is why declarative has gotten so crazy compared to its innocent beginnings. This might be something that can be added but I'd have to think about it, ABC is still pretty brittle overall.

I can have you just use the API that ABC uses internally. Concrete mappings in classical SQLA were really easy, because we had those Table objects up front before we did anything with the classes. With declarative we don't have that because it makes the table/mapper at the same time. This architecture has opened up a lot in 1.0 but still doesn't make this kind of thing that simple. But the main thing that was added probably in 0.8 or 0.9 to make this possible was a way to attach the "base" underneath a concrete mapper after the subclass is set up. Instead of ABC doing that for us, we can do it the "old" way manually, using polymophic_union() in the old way and calling mapper(), just using one newish API function so that we can still use declarative for the subclasses. It's one private API function at the moment. It maps and creates the queries, so that should be pretty much it - we can try to make these API patterns more accessible. Let me know if this works more fully.

from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.ext.declarative.api import declared_attr
from sqlalchemy.orm import configure_mappers, mapper, Session
from sqlalchemy.sql.schema import Column, ForeignKey
from sqlalchemy.sql.sqltypes import Date, String, Integer

Base = declarative_base()


class Company(Base):
    __tablename__ = 'companies'
    id = Column(Integer, primary_key=True)


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

    __abstract__ = True
    __mapper_args__ = {"concrete": True}


class SomeDocument(Document):
    """extends Document but not ContactDocument """
    __tablename__ = 'some_document'

    id = Column(Integer, primary_key=True)


class ContactDocument(Document):
    contactPersonName = Column(String)
    salesPersonName = Column(String)
    sendMethod = Column(String)

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

    __abstract__ = True


class Offer(ContactDocument):
    __tablename__ = 'offers'

    id = Column(Integer, primary_key=True)


class SalesOrder(ContactDocument):
    __tablename__ = 'orders'

    id = Column(Integer, primary_key=True)


from sqlalchemy.orm.util import polymorphic_union

document_pjoin = polymorphic_union({
    'offer': Offer.__table__,
    'orders': SalesOrder.__table__,
    'somedocument': SomeDocument.__table__
}, 'type', 'd_pjoin'
)

contact_document_pjoin = polymorphic_union({
    'offer': Offer.__table__,
    'orders': SalesOrder.__table__,
}, 'type', 'cd_pjoin'
)

md = mapper(
    Document,
    document_pjoin,
    polymorphic_on=document_pjoin.c.type,
    concrete=True)
mcd = mapper(
    ContactDocument,
    contact_document_pjoin,
    inherits=md,
    polymorphic_on=contact_document_pjoin.c.type,
    concrete=True)

# AbstractConcreteBase does this part by looking at cls.__subclasses__()
Offer.__mapper__._set_concrete_base(mcd)
SalesOrder.__mapper__._set_concrete_base(mcd)
SomeDocument.__mapper__._set_concrete_base(md)

configure_mappers()
session = Session()
print "-----------"
print session.query(Document)
print "-----------"
print session.query(ContactDocument)





torstai 9. heinäkuuta 2015 18.31.36 UTC+3 Michael Bayer kirjoitti:

    Thanks for reporting.   Issue
    
https://bitbucket.org/zzzeek/sqlalchemy/issues/3480/abstractconcretebase-regression-with
    is created, create the Column objects with an explicit key for now:


    class Document(object):
        date = Column(Date)
        documentType = Column('documenttype', String, key="documentType")


    class ContactDocument(AbstractConcreteBase, Base, Document):
        contactPersonName = Column('contactpersonname', String,
    key="contactPersonName")
        salesPersonName = Column(String)
        sendMethod = Column('sendmethod', String, key="sendMethod")

        @declared_attr
        def company_id(self):
            return Column(ForeignKey('companies.id
    <http://companies.id>'))



    On 7/9/15 11:18 AM, Alex Grönholm wrote:
    The following script no longer works in 1.0.6, but does in 0.9.9:


    fromsqlalchemy.ext.declarativeimportdeclarative_base, AbstractConcreteBase
    fromsqlalchemy.ext.declarative.apiimportdeclared_attr
    fromsqlalchemy.orm.mapperimportconfigure_mappers
    fromsqlalchemy.orm.sessionimportSession
    fromsqlalchemy.sql.schemaimportColumn, ForeignKey
    fromsqlalchemy.sql.sqltypesimportDate, String, Integer

    Base = declarative_base()


    classCompany(Base):
         __tablename__ ='companies'
         id = Column(Integer,primary_key=True)


    classDocument(object):
         date = Column(Date)
         documentType = Column('documenttype', String)


    classContactDocument(AbstractConcreteBase, Base, Document):
         contactPersonName = Column('contactpersonname', String)
         salesPersonName = Column(String)
         sendMethod = Column('sendmethod', String)

         @declared_attr
         defcompany_id(self):
             returnColumn(ForeignKey('companies.id  <http://companies.id>'))


    classOffer(ContactDocument):
         __tablename__ ='offers'

         id = Column(Integer,primary_key=True)


    classSalesOrder(ContactDocument):
         __tablename__ ='orders'

         id = Column(Integer,primary_key=True)


    configure_mappers()
    session = Session()
    query = session.query(ContactDocument)
    print(query)


    On 1.0.6, I get an error: sqlalchemy.exc.ArgumentError: When
    configuring property 'documentType' on
    Mapper|ContactDocument|pjoin, column 'documenttype' is not
    represented in the mapper's table. Use the `column_property()`
    function to force this column to be mapped as a read-only attribute.
    Why am I getting this? Is this a bug or am I not understanding
    something?

    Also, is it possible to have both Document and ContactDocument
    as abstract concrete base classes (ie. I want the union from
    Document to include both the direct concrete subclasses of
    Document and all concrete subclasses of ContactDocument as well)?
-- You received this message because you are subscribed to the
    Google Groups "sqlalchemy" group.
    To unsubscribe from this group and stop receiving emails from
    it, send an email to [email protected] <javascript:>.
    To post to this group, send email to [email protected].
    Visit this group at http://groups.google.com/group/sqlalchemy.
    For more options, visit https://groups.google.com/d/optout.

--
You received this message because you are subscribed to the Google Groups "sqlalchemy" group. To unsubscribe from this group and stop receiving emails from it, send an email to [email protected] <mailto:[email protected]>. To post to this group, send email to [email protected] <mailto:[email protected]>.
Visit this group at http://groups.google.com/group/sqlalchemy.
For more options, visit https://groups.google.com/d/optout.

--
You received this message because you are subscribed to a topic in the Google Groups "sqlalchemy" group. To unsubscribe from this topic, visit https://groups.google.com/d/topic/sqlalchemy/U8mpVm8udi8/unsubscribe. To unsubscribe from this group and all its topics, send an email to [email protected] <mailto:[email protected]>. To post to this group, send email to [email protected] <mailto:[email protected]>.
Visit this group at http://groups.google.com/group/sqlalchemy.
For more options, visit https://groups.google.com/d/optout.

--
You received this message because you are subscribed to the Google Groups 
"sqlalchemy" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To post to this group, send email to [email protected].
Visit this group at http://groups.google.com/group/sqlalchemy.
For more options, visit https://groups.google.com/d/optout.

Reply via email to