如何在flask-sqlalchemy中实现三方多对多关系

13 投票
2 回答
3015 浏览
提问于 2025-04-18 02:31

在flask-sqlalchemy中,如何正确设计一个三方多对多的关系?

假设我有用户、团队和角色。用户被分配到团队中。当用户被分配到一个团队时,他们在这个团队中也会被分配一个角色。

from myapp import db

class User(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(128), unique=True)

    def __init__(self, name):
        self.name = name

    def __repr__(self):
        return "<User(%s)>" % self.name

class Team(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(128), unique=True)

    def __init__(self, name):
        self.name = name

    def __repr__(self):
        return "<Team(%s)>" % self.name

class Role(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(128), unique=True)

    def __init__(self, name):
        self.name = name

    def __repr__(self):
        return "<Role(%s)>" % self.name

我尝试了几种方法来设计一个代理表,但都没有成功。我觉得我可能误解了文档,所以不想把我之前失败的方法告诉你,以免让你更困惑。

我想留下一个问题:在flask-sqlalchemy中,如何正确设计一个三方多对多的关系?

2 个回答

-3

伙计们。解决两个多对多关系问题的官方方法是使用一个辅助表。

Helper_table = db.Table('Helper_table',
                     db.Column('id_a', db.Integer,
                               db.ForeignKey('a.id')),
                     db.Column('id_b', db.Integer,
                               db.ForeignKey('b.id'))
                )
class A(db.Model):  # A ORM
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(64), index=True, unique=True)

    bs = db.relationship(
        'B', secondary=Helper_table, lazy='dynamic')

    def __repr__(self):
        return '<A {}>'.format(self.username)

class B(db.Model):  # B ORM
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(64), index=True, unique=True)


    as = db.relationship(
        'A', secondary=Helper_table, lazy='dynamic')

    def __repr__(self):
        return '<B {}>'.format(self.username)

不过,这个方法只能解决两个多对多的问题!!!它会自动处理这些关系。

a1 = A(username="a1_"+str(uuid.uuid4()))
b1 = B(username="b1_"+str(uuid.uuid4()))

a1.bs.append(b1)
db.session.add(a1)
db.session.commit()

就像上面的代码所示,它会自动把 b1 加入数据库,还有 b1.as

但是当我尝试在三个多对多关系的情况下使用这个方法时,一切都乱套了。

这是我问的问题:在flask-sqlalchemy中处理多对多对多关系的多个辅助表

总的来说,我觉得这应该是这个库的一个功能。应该有一种优雅的方法来处理这个问题。

20

经过大量的研究和探索,我终于找到了答案。因为我发现很多人也在为这个问题苦恼,却找不到一个完整清晰的解答,所以我决定把这个分享出来,方便以后遇到这个问题的人。

如果你也在问这个问题,可能你并不是真的在寻找一个三方多对多的关系。我原以为我在找这个,但其实并不是。

总结一下:我有用户、团队和角色。如果一个用户加入了一个团队,他在这个团队里也会被分配一个角色。

我回到白板上,画出了我真正想要的东西:

+---------+---------+---------+
| user_id | team_id | role_id |
+---------+---------+---------+
|       1 |       1 |       1 |
+---------+---------+---------+

然后我开始明白,其实我并不需要一个三方多对多的关系,而是需要一个三方一对多的关系,并且是从一个第四个模型出发。

class Membership(db.Model):
    user_id = db.Column(db.Integer, db.ForeignKey('user.id'), primary_key=True)
    team_id = db.Column(db.Integer, db.ForeignKey('team.id'), primary_key=True)
    role_id = db.Column(db.Integer, db.ForeignKey('role.id'), primary_key=True)

    db.UniqueConstraint('user_id', 'team_id', 'role_id')
    db.relationship('User', uselist=False, backref='memberships', lazy='dynamic')
    db.relationship('Team', uselist=False, backref='memberships', lazy='dynamic')
    db.relationship('Role', uselist=False, backref='memberships', lazy='dynamic')

    def __init__(self, user, team, role):
        self.user_id = user.id
        self.team_id = team.id
        self.role_id = role.id

    def __repr__(self):
        return "<Membership(%s)>"

案例42:这正是我一直在寻找的答案——我只是问错了问题。

撰写回答