Ahh ok i already asumed that it has something to do with the "declarative" approch. I think i will postpone that feature in my app until 1.0 is released.
Thanks Josip On 4 November 2014 17:50, Michael Bayer <mike...@zzzcomputing.com> wrote: > OK, coffee has been applied. This mapping can be done in 0.9 but only > if you use classical mappings, AbstractConcreteBase and declarative aren’t > ready yet. In 1.0, I made a lot of improvements (see > http://docs.sqlalchemy.org/en/latest/changelog/migration_10.html#improvements-to-declarative-mixins-declared-attr-and-related-features) > that manage to get this to work using a fully declarative approach. A > minimal example is below. In particular, you *don’t* want to have a > “polymorphic_on” column added to any specific table when using concrete > mappings, there’s no base table that’s shared. The AbstractConcreteBase > produces this “polymorphic_on” for you, and it’s only part of the > “polymorphic union” query you see in the SQL output of the script: > > > from sqlalchemy import Column, String, Integer, create_engine, ForeignKey, > Float > from sqlalchemy.orm import Session, relationship > from sqlalchemy.ext.declarative import declarative_base > from sqlalchemy.orm.collections import attribute_mapped_collection > from sqlalchemy.ext.declarative import declared_attr, AbstractConcreteBase > > > Base = declarative_base() > > > class Mammut(Base): > __tablename__ = "mammut" > > id = Column(Integer, primary_key=True) > nodes = relationship( > 'TreeNode', > lazy='dynamic', > back_populates='mammut', > ) > > > class TreeNode(AbstractConcreteBase, Base): > id = Column(Integer, primary_key=True) > name = Column(String) > > @declared_attr > def __tablename__(cls): > if cls.__name__ == 'TreeNode': > return None > else: > return cls.__name__.lower() > > @declared_attr > def __mapper_args__(cls): > return {'polymorphic_identity': cls.__name__, 'concrete': True} > > @declared_attr > def parent_id(cls): > return Column(Integer, ForeignKey(cls.id)) > > @declared_attr > def mammut_id(cls): > return Column(Integer, ForeignKey('mammut.id')) > > @declared_attr > def mammut(cls): > return relationship("Mammut", back_populates="nodes") > > @declared_attr > def children(cls): > return relationship( > cls, > back_populates="parent", > collection_class=attribute_mapped_collection('name'), > ) > > @declared_attr > def parent(cls): > return relationship( > cls, remote_side="%s.id" % cls.__name__, > back_populates='children') > > > class IntTreeNode(TreeNode): > value = Column(Integer) > > > class FloatTreeNode(TreeNode): > value = Column(Float) > miau = Column(String(50), default='zuff') > > e = create_engine("sqlite://", echo=True) > Base.metadata.create_all(e) > > session = Session(e) > > root = IntTreeNode(name='root') > IntTreeNode(name='n1', parent=root) > n2 = IntTreeNode(name='n2', parent=root) > IntTreeNode(name='n2n1', parent=n2) > > m1 = Mammut() > m1.nodes.append(n2) > m1.nodes.append(root) > > session.add(root) > session.commit() > > > session.close() > > root = session.query(TreeNode).filter_by(name='root').one() > print root.children > > > that polymorphic query at the bottom looks like the following, note that > the “type”, which is our polymorphic_on, is a virtual column: > > SELECT pjoin.id AS pjoin_id, pjoin.name AS pjoin_name, pjoin.value AS > pjoin_value, pjoin.miau AS pjoin_miau, pjoin.mammut_id AS pjoin_mammut_id, > pjoin.parent_id AS pjoin_parent_id, pjoin.type AS pjoin_type > FROM (SELECT floattreenode.id AS id, floattreenode.name AS name, > floattreenode.value AS value, floattreenode.miau AS miau, > floattreenode.mammut_id AS mammut_id, floattreenode.parent_id AS parent_id, > 'FloatTreeNode' AS type > FROM floattreenode UNION ALL SELECT inttreenode.id AS id, inttreenode.name > AS name, inttreenode.value AS value, CAST(NULL AS VARCHAR(50)) AS miau, > inttreenode.mammut_id AS mammut_id, inttreenode.parent_id AS parent_id, > 'IntTreeNode' AS type > FROM inttreenode) AS pjoin > WHERE pjoin.name = ? > > > > > > > > > On Nov 4, 2014, at 10:33 AM, Michael Bayer <mike...@zzzcomputing.com> > wrote: > > there is a lot lot lot going on here. The example isn’t working in 1.0 > for different reasons, for example. > > However lets start with just the error you have, and to do that, lets > please just show the minimal amount of code to reproduce: > > from sqlalchemy import * > from sqlalchemy.orm import * > from sqlalchemy.ext.declarative import declarative_base > from sqlalchemy.ext.declarative import declared_attr, AbstractConcreteBase > > > Base = declarative_base() > > > class Mammut(Base): > __tablename__ = "mammut" > > id = Column(Integer, primary_key=True) > nodes = relationship( > 'TreeNode', > backref='mammut', > ) > > > class TreeNode(AbstractConcreteBase, Base): > id = Column(Integer, primary_key=True) > name = Column(String(50), nullable=False) > depth = Column(Integer, default=0) > data_type = Column(String(50)) > > @declared_attr > def mammut_id(cls): > return Column(Integer, ForeignKey('mammut.id')) > > def __init__(self, name, value=None, parent=None): > self.name = name > self.parent = parent > self.depth = 0 > self.value = value > if self.parent: > self.depth = self.parent.depth + 1 > > > class IntTreeNode(TreeNode): > value = Column(Integer) > > __tablename__ = 'int' > __mapper_args__ = {"concrete": True, "polymorphic_identity": 'int'} > > > class FloatTreeNode(TreeNode): > value = Column(Float) > miau = Column(String(50), default='zuff') > > __tablename__ = 'float' > __mapper_args__ = {"concrete": True, "polymorphic_identity": 'float'} > > node = IntTreeNode('rootnode', value=2) > > mut = Mammut() > mut.nodes.append(node) > > The issue is that you can’t just do a single “backref” to concrete > classes. You have to use the instructions at > http://docs.sqlalchemy.org/en/rel_0_9/orm/inheritance.html#relationships-with-concrete-inheritance > to > create each reference from IntTreeNode / FloatTreeNode etc. back. > > This has been improved in 1.0. We can say: > > class Mammut(Base): > __tablename__ = "mammut" > > id = Column(Integer, primary_key=True) > nodes = relationship( > 'TreeNode', > back_populates='mammut', > ) > > class TreeNode(AbstractConcreteBase, Base): > id = Column(Integer, primary_key=True) > name = Column(String(50), nullable=False) > depth = Column(Integer, default=0) > data_type = Column(String(50)) > > @declared_attr > def mammut_id(cls): > return Column(Integer, ForeignKey('mammut.id')) > > @declared_attr > def mammut(cls): > return relationship("Mammut", back_populates='nodes') > > def __init__(self, name, value=None, parent=None): > self.name = name > self.parent = parent > self.depth = 0 > self.value = value > if self.parent: > self.depth = self.parent.depth + 1 > > however in 0.9, this won’t work, and after some experimentation I don’t > think it’s possible in 0.9 to have a backref pointing to an > AbstractConcreteBase, sorry. > > Also, the attempt to make a self-referential relationship from TreeNode to > itself is also not possible in the way you are attempting. There is no > TreeNode table, so this would require distinct relationships and foreign > keys on each concrete table, however I’m not getting that to work either. > > I think you might not intend to use AbstractConcreteBase here in any case > as it seems like you want there to be a base table (this creates one?). > > > I’ll try to look more later but overall there’s kind of too much going on > here and concrete inheritance is not very easy to use, sorry. > > > > > > > > > > > > > On Nov 4, 2014, at 7:28 AM, delijati <delij...@gmail.com> wrote: > > Hello, > > i posted my question on stakoverflow. So to not repeat myself: > > > https://stackoverflow.com/questions/26724897/adjacency-list-abstract-base-class-inheritance-used-in-relationship > > Josip > > > -- > 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 sqlalchemy+unsubscr...@googlegroups.com. > To post to this group, send email to sqlalchemy@googlegroups.com. > 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 sqlalchemy+unsubscr...@googlegroups.com. > To post to this group, send email to sqlalchemy@googlegroups.com. > 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 sqlalchemy+unsubscr...@googlegroups.com. > To post to this group, send email to sqlalchemy@googlegroups.com. > 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 sqlalchemy+unsubscr...@googlegroups.com. To post to this group, send email to sqlalchemy@googlegroups.com. Visit this group at http://groups.google.com/group/sqlalchemy. For more options, visit https://groups.google.com/d/optout.