I’ve found examples for a self-referencing many-to-many relationship with an association table. How can I achieve the same using an association object?
The code below is based on: How can I achieve a self-referencing many-to-many relationship on the SQLAlchemy ORM back referencing to the same attribute?
JavaScript
x
35
35
1
from sqlalchemy import Table, Column, Integer, ForeignKey
2
from db.common import Base
3
from sqlalchemy.orm import relationship
4
5
M2M = Table('m2m',
6
Base.metadata,
7
Column('entity_parent_id',
8
Integer,
9
ForeignKey('entity.id'),
10
primary_key=True),
11
Column('entity_child_id',
12
Integer,
13
ForeignKey('entity.id'),
14
primary_key=True),
15
)
16
17
18
class Entity(Base):
19
__tablename__ = 'entity'
20
21
id = Column(Integer, primary_key=True)
22
23
entity_childs = relationship("Entity",
24
secondary=M2M,
25
primaryjoin="Enity.id==m2m.c.entity_parent_id",
26
secondaryjoin="Enity.id==m2m.c.entity_child_id",
27
)
28
29
entity_parents = relationship("Entity",
30
secondary=M2M,
31
primaryjoin="Enity.id==m2m.c.entity_child_id",
32
secondaryjoin="Enity.id==m2m.c.entity_parent_id",
33
)
34
35
Advertisement
Answer
The following approach uses an association object instead of an association table to get a self-referencing many-to-many relationship:
JavaScript
1
61
61
1
from sqlalchemy import Column, Integer, ForeignKey, create_engine, String
2
from sqlalchemy.orm import relationship, sessionmaker
3
from sqlalchemy.ext.declarative import declarative_base
4
5
Base = declarative_base()
6
7
class EntityAssociation(Base):
8
__tablename__ = 'entity_association'
9
10
entity_parent_id = Column(Integer, ForeignKey('entity.id'), primary_key=True)
11
entity_child_id = Column(Integer, ForeignKey('entity.id'), primary_key=True)
12
13
class Entity(Base):
14
__tablename__ = 'entity'
15
16
id = Column(Integer, primary_key=True)
17
name = Column(String)
18
19
entity_childs = relationship('Entity',
20
secondary='entity_association',
21
primaryjoin=id==EntityAssociation.entity_parent_id,
22
secondaryjoin=id==EntityAssociation.entity_child_id,
23
backref='childs')
24
25
entity_parents = relationship('Entity',
26
secondary='entity_association',
27
primaryjoin=id==EntityAssociation.entity_child_id,
28
secondaryjoin=id==EntityAssociation.entity_parent_id,
29
backref='parents')
30
31
def __repr__(self):
32
return f'<Entity(name={self.name})>'
33
34
if __name__ == '__main__':
35
engine = create_engine('sqlite://')
36
Base.metadata.create_all(engine)
37
Session = sessionmaker(engine)
38
39
db = Session()
40
41
parent1 = Entity(name='parent1')
42
parent2 = Entity(name='parent2')
43
child1 = Entity(name='child1')
44
child2 = Entity(name='child2')
45
46
parent1.entity_childs = [child1, child2]
47
parent2.entity_childs = [child2]
48
49
db.add(parent1)
50
db.add(parent2)
51
db.add(child1)
52
db.add(child2)
53
db.commit()
54
55
entities = db.query(Entity).all()
56
for entity in entities:
57
print(entity)
58
print(' Parent: ', entity.entity_parents)
59
print(' Childs: ', entity.entity_childs)
60
print()
61
This will have the following result:
JavaScript
1
16
16
1
<Entity(name=parent1)>
2
Parent: []
3
Childs: [<Entity(name=child1)>, <Entity(name=child2)>]
4
5
<Entity(name=child1)>
6
Parent: [<Entity(name=parent1)>]
7
Childs: []
8
9
<Entity(name=child2)>
10
Parent: [<Entity(name=parent1)>, <Entity(name=parent2)>]
11
Childs: []
12
13
<Entity(name=parent2)>
14
Parent: []
15
Childs: [<Entity(name=child2)>]
16