single table inheritance does not work with multiple inheritance

276 views
Skip to first unread message

Ethan Fremen

unread,
Jan 14, 2013, 1:09:11 PM1/14/13
to sqlal...@googlegroups.com
I have a pretty simple case. A base class with three sub-classes, and then a final class that inherits from the three derived classes. When persisting, sqlalchemy only sets the properties of the parent class and the first child class.

In all other respects the sub-class is behaving properly; it has the correct attributes, etc.

So, if EvidenceLineItem inherits from EvidenceString first, only the string property (and the properties in Evidence) will update; if I change the first inherited class to EvidenceAmount, amount will be persisted, but the rest not.

I'll try declared_attr, but I'm pretty sure that this behaviour isn't what is intended.

~ethan

class Evidence(Base):
    __tablename__ = "evidence"
    discriminator = Column(DBEnum(ProofItemClassType), nullable=False) 
    __mapper_args__ = { 'polymorphic_on': discriminator,
                        'polymorphic_identity': ProofItemClassType.generic }

class EvidenceString(Evidence):
    __mapper_args__ = { 'polymorphic_identity': ProofItemClassType.string }
    string = Column(String(length=255), index=True)

class EvidenceAmount(Evidence):
    __mapper_args__ = { 'polymorphic_identity': ProofItemClassType.amount }
    amount = Column(Numeric(precision=7, scale=2), index=True)

class EvidenceQuantity(Evidence):
    __mapper_args__ = { 'polymorphic_identity': ProofItemClassType.quantity }
    quantity = Column(Integer)

class EvidenceLineItem(EvidenceString, EvidenceAmount, EvidenceQuantity):
    __mapper_args__ = { 'polymorphic_identity': ProofItemClassType.line_item }

Ethan Fremen

unread,
Jan 14, 2013, 1:13:25 PM1/14/13
to sqlal...@googlegroups.com
using declared_attr instead of declaring the attributes directly did not help. now I'm going to try the strategy of referring to the existing table attribute.

(on 0.8.2b2, fwiw)

~ethan

Michael Bayer

unread,
Jan 14, 2013, 1:17:55 PM1/14/13
to sqlal...@googlegroups.com
The inheritance mechanics don't support multiple inheritance from the POV of multiple mapped classes as the base for another mapped class.   Single table inheritance would be the easiest case to work at some point, but there's lots of mechanics in place right now that assume a linear path from subclass to superclass.

If you don't actually need instances of EvidenceString, EvidenceAmount, and EvidenceQuantity to exist in the database distinctly (that is, you'd never actually have any rows with "string", "amount", or "quantity" present in the DB as the discriminator), you'd define these as unmapped mixins, which is very well supported (see http://docs.sqlalchemy.org/en/rel_0_8/orm/extensions/declarative.html#mixin-and-custom-base-classes) .   It seems here like that's what you're really looking for.



--
You received this message because you are subscribed to the Google Groups "sqlalchemy" group.
To view this discussion on the web visit https://groups.google.com/d/msg/sqlalchemy/-/5hEAmVfC-EYJ.
To post to this group, send email to sqlal...@googlegroups.com.
To unsubscribe from this group, send email to sqlalchemy+...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/sqlalchemy?hl=en.

Michael Bayer

unread,
Jan 14, 2013, 1:21:59 PM1/14/13
to sqlal...@googlegroups.com
and if you expand upon this approach you'll probably have column conflicts too, refer to http://docs.sqlalchemy.org/en/rel_0_8/orm/extensions/declarative.html#resolving-column-conflicts for the strategy for dealing with that.

Ethan Fremen

unread,
Jan 14, 2013, 1:37:39 PM1/14/13
to sqlal...@googlegroups.com
I am all down with the power of mixins. The reason I mapped things this way is because I do have concrete instances of string, amount, and quantity.

(I'm capturing data from receipts: "string" can be e.g. a receipt number, amount can be e.g. a total, and quantity can be something unaffiliated with a line item, like "number of seats reserved".

I finally got this to work by re-declaring all the properties I wanted using declared_attr on the "final" class, like the below.  Which is fine in this case, but slightly more annoying in the other case I have, where there are quite a few inherited properties.

-ethan

class EvidenceLineItem(EvidenceAmount, EvidenceQuantity, EvidenceString):
    __mapper_args__ = { 'polymorphic_identity': ProofItemClassType.line_item }
    @declared_attr
    def string(cls):
        return Evidence.__table__.c.get('string',Column(String(length=255), index=True))
    @declared_attr
    def quantity(cls):
        return Evidence.__table__.c.get('quantity',Column(Integer))
    @declared_attr
    def amount(cls):
        return Evidence.__table__.c.get('amount',Column(Integer, index=True))

Michael Bayer

unread,
Jan 14, 2013, 2:00:55 PM1/14/13
to sqlal...@googlegroups.com
I agree this could be better.   Not sure how deep we'd have to go to get multiple inheritance in single inheritance form to work, mapping to inheritance hierarchies is probably what adds most complexity to the ORM as it is.   It's a very rare use case though, and also gets way harder when we start talking about joined inheritance which is generally supported in the same hierarchy as single,  so it's hard to prioritize this feature.



--
You received this message because you are subscribed to the Google Groups "sqlalchemy" group.
To view this discussion on the web visit https://groups.google.com/d/msg/sqlalchemy/-/VuHxC54meNsJ.
Reply all
Reply to author
Forward
0 new messages