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)