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
<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 <[email protected]> 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
>
> <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 <[email protected]
>> <mailto:[email protected]>> 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
>>
>> <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 [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
>> <http://groups.google.com/group/sqlalchemy>.
>> For more options, visit https://groups.google.com/d/optout
>> <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
> <http://groups.google.com/group/sqlalchemy>.
> For more options, visit https://groups.google.com/d/optout
> <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.