繁体   English   中英

如何在 Flask-SQLAlchemy 中同时设置一对多和一对一关系?

[英]How to set one to many and one to one relationship at same time in Flask-SQLAlchemy?

我正在尝试在 Flask-SQLAlchemy 中同时创建一对一和一对多关系。 我想实现这个:

“一个群组有很多成员和一个管理员。”

这是我所做的:

class Group(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(140), index=True, unique=True)
    description = db.Column(db.Text)
    created_at = db.Column(db.DateTime, server_default=db.func.now())

    members = db.relationship('User', backref='group')
    admin = db.relationship('User', backref='admin_group', uselist=False)

    def __repr__(self):
        return '<Group %r>' % (self.name)


class User(db.Model):
    id = db.Column(db.Integer, primary_key=True)

    group_id = db.Column(db.Integer, db.ForeignKey('group.id'))
    admin_group_id = db.Column(db.Integer, db.ForeignKey('group.id'))

    created_at = db.Column(db.DateTime, server_default=db.func.now())

但是我得到一个错误:

sqlalchemy.exc.AmbiguousForeignKeysError:无法确定关系 Group.members 上父/子表之间的连接条件 - 有多个外键路径链接表。 指定 'foreign_keys' 参数,提供应计为包含对父表的外键引用的那些列的列表。

有谁知道如何正确地做到这一点?

您遇到的问题来自于您在类之间定义了两个链接 - 一个用户有一个group_id(一个外键),一个组有一个admin(也是一个外键定义) 。 如果从管理字段中删除外键,则连接不再模糊,并且关系有效。 这是我的问题解决方案(使链接一对一):

from app import db,app

class Group(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(140), index=True, unique=True)
    description = db.Column(db.Text)
    created_at = db.Column(db.DateTime, server_default=db.func.now())
    admin_id = db.Column(db.Integer) #, db.ForeignKey('user.id'))
    members = db.relationship('User', backref='group')

    def admin(self):
        return User.query.filter_by(id=self.admin_id).first()

class User(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(80), unique=True)
    group_id = db.Column(db.Integer, db.ForeignKey('group.id'))
    created_at = db.Column(db.DateTime, server_default=db.func.now())

这样做的一个缺点是组对象没有你可以使用的简洁admin成员对象 - 你必须调用函数group.admin()来检索管理员。 但是,该组可以有许多成员,但只有其中一个可以是管理员。 显然,没有DB级别的检查来确保管理员实际上是该组的成员,但您可以将该检查添加到setter函数中 - 可能类似于:

# setter method
def admin(self, user):
    if user.group_id == self.id:
        self.admin_id = user.id

# getter method
def admin(self):
    return User.query.filter_by(id=self.admin_id).first()

解决方案是在所有relationship s上指定foreign_keys参数:

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

    group_id = Column(Integer, ForeignKey('groups.id'))
    admin_group_id = Column(Integer, ForeignKey('groups.id'))

class Group(Base):
    __tablename__ = 'groups'
    id = Column(Integer, primary_key=True)

    members = relationship('User', backref='group', foreign_keys=[User.group_id])
    admin = relationship('User', backref='admin_group', uselist=False, foreign_keys=[User.admin_group_id])

也许考虑在另一个方向的管理关系来实现“一个组有很多成员和一个管理员”:

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

    group_id = Column(Integer, ForeignKey('groups.id'))
    group = relationship('Group', foreign_keys=[group_id], back_populates='members')


class Group(Base):
    __tablename__ = 'groups'
    id = Column(Integer, primary_key=True)

    members = relationship('User', foreign_keys=[User.group_id], back_populates='group')

    admin_user_id = Column(Integer, ForeignKey('users.id'))
    admin = relationship('User', foreign_keys=[admin_user_id], post_update=True)

请参阅文档中有关post_update 的说明 当两个模型相互依赖,相互引用时,这是必要的。

好的,我终于找到了解决这个问题的方法。 多对多关系可以同时与同一两个表之间的一对多关系共存。

这是代码:

groups_admins = db.Table('groups_admins',
                         db.Column('user_id', db.Integer, db.ForeignKey('user.id')),
                         db.Column('group_id', db.Integer, db.ForeignKey('group.id'))
                        )


class Group(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(140), index=True, unique=True)
    description = db.Column(db.Text)
    created_at = db.Column(db.DateTime, server_default=db.func.now())
    members = db.relationship('User', backref='group')
    admins = db.relationship('User',
                             secondary=groups_admins,
                             backref=db.backref('mod_groups', lazy='dynamic'),
                             lazy='dynamic')

    def __repr__(self):
        return '<Group %r>' % (self.name)


class User(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    group_id = db.Column(db.Integer, db.ForeignKey('group.id'))
    created_at = db.Column(db.DateTime, server_default=db.func.now())

我仍然希望有人告诉我如何同时设置一对多和一对一的关系,所以我在这里留下我的答案,不会永远接受它。

这个链接帮我解决了

最重要的是在关系中指定 foreign_keys 值以及主连接

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM