Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SQLAlchemy One-to-Many relationship on single table inheritance - declarative

Basically, I have this model, where I mapped in a single table a "BaseNode" class, and two subclasses. The point is that I need one of the subclasses, to have a one-to-many relationship with the other subclass. So in sort, it is a relationship with another row of different class (subclass), but in the same table. How do you think I could write it using declarative syntax?.

Note: Due to other relationships in my model, if it is possible, I really need to stick with single table inheritance.

class BaseNode(DBBase):
    __tablename__ = 'base_node'
    id = Column(Integer, primary_key=True)
    discriminator = Column('type', String(50))
    __mapper_args__ = {'polymorphic_on': discriminator}

class NodeTypeA(BaseNode):
    __mapper_args__ = {'polymorphic_identity': 'NodeTypeA'}
    typeB_children = relationship('NodeTypeB', backref='parent_node')


class NodeTypeB(BaseNode):
    __mapper_args__ = {'polymorphic_identity': 'NodeTypeB'}
    parent_id = Column(Integer, ForeignKey('base_node.id'))

Using this code will throw:

sqlalchemy.exc.ArgumentError: NodeTypeA.typeB_children and back-reference NodeTypeB.parent_node are both of the same direction . Did you mean to set remote_side on the many-to-one side ?

Any ideas or suggestions?

like image 790
pedro.guridi Avatar asked Jul 21 '11 20:07

pedro.guridi


1 Answers

I was struggling through this myself earlier. I was able to get this self-referential relationship working:

class Employee(Base):
  __tablename__ = 'employee'
  id = Column(Integer, primary_key=True)
  name = Column(String(64), nullable=False)
Employee.manager_id = Column(Integer, ForeignKey(Employee.id))
Employee.manager = relationship(Employee, backref='subordinates',
    remote_side=Employee.id)

Note that the manager and manager_id are "monkey-patched" because you cannot make self-references within a class definition.

So in your example, I would guess this:

class NodeTypeA(BaseNode):
    __mapper_args__ = {'polymorphic_identity': 'NodeTypeA'}
    typeB_children = relationship('NodeTypeB', backref='parent_node',
        remote_side='NodeTypeB.parent_id')

EDIT: Basically what your error is telling you is that the relationship and its backref are both identical. So whatever rules that SA is applying to figure out what the table-level relationships are, they don't jive with the information you are providing.

I learned that simply saying mycolumn=relationship(OtherTable) in your declarative class will result in mycolumn being a list, assuming that SA can detect an unambiguous relationship. So if you really want an object to have a link to its parent, rather than its children, you can define parent=relationship(OtherTable, backref='children', remote_side=OtherTable.id) in the child table. That defines both directions of the parent-child relationship.

like image 98
wberry Avatar answered Oct 08 '22 02:10

wberry