Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SQLAlchemy Relationship Join with Multiple Foreign Keys

Using SQLAlchemy, I'm having trouble figuring out how to map the following relation: a user can be 'followed' by other users, thus becoming a 'leader' with 'followers'.

class User( Base ):
    __tablename__ = 'users'
    id = Column( Integer, primary_key = True )
    followers = relationship( 'User', primaryjoin = 'and_( User.id == UserFollower.leader_id )', cascade = 'all' )
    leaders = relationship( 'User', primaryjoin = 'and_( User.id == UserFollower.follower_id )', cascade = 'all' )

class UserFollower( Base ):
    __tablename__ = 'users_followers'
    leader_id = Column( Integer, ForeignKey( 'users.id' ), primary_key = True )
    follower_id = Column( Integer, ForeignKey( 'users.id' ), primary_key = True )
    follower = relationship( 'User', uselist = False, foreign_keys = [ follower_id ] )
    leader = relationship( 'User', uselist = False, foreign_keys = [ leader_id ] )

From the documentation I was under the impression that such a declaration would have

some_user.followers

perform a join of the 'users' and 'users_followers' tables on 'users.id = users_followers.leader_id' where 'user.id = some_user.id'.

However, this yields an error (similar to How to specify table relationships in SQLAlchemy with multi-level/multiple joins?). Although the proposed solution allows me to work around this issue, it prevents the use of the 'cascading' property of SQLAlchemy's relationships so that when deleting a User, the corresponding UserFollower objects are also deleted.

In summary: how can I have relationships joined to a table with multiple foreign keys referring to the same key and still preserve cascading (deletion)?

like image 568
dgrine Avatar asked May 06 '26 05:05

dgrine


1 Answers

If you read the Self-Referential Many-to-Many Relationship section of SQLAlchemy documentation, you will realise that you have a classical many-to-many relationship. If you define this relation according to the documentation, the cascades will work just fine:

users_followers = Table(
    'users_followers', Base.metadata,
    Column('leader_id', ForeignKey('users.id'), primary_key=True),
    Column('follower_id', ForeignKey('users.id'), primary_key=True),
)


class User(Base):
    __tablename__ = 'users'
    id = Column(Integer, primary_key=True)
    name = Column(String)

    followers = relationship(
        'User',
        secondary=users_followers,
        primaryjoin=id == users_followers.c.leader_id,
        secondaryjoin=id == users_followers.c.follower_id,
        backref="leaders",
        cascade='all',
    )
like image 53
van Avatar answered May 09 '26 05:05

van



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!