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

Vay*_*ayn 7 python sqlalchemy flask 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())
Run Code Online (Sandbox Code Playgroud)

但是我收到了一个错误:

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

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

Aar*_*n D 1

您遇到的问题来自于您在类之间定义了两个链接 - 用户有一个 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())
Run Code Online (Sandbox Code Playgroud)

这样做的一个缺点是组对象没有一个admin可供您使用的简洁的成员对象 - 您必须调用该函数group.admin()来检索管理员。但是,该组可以有多个成员,但只有其中一名成员可以担任管理员。显然,没有数据库级别的检查来确保管理员实际上是该组的成员,但您可以将该检查添加到 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()
Run Code Online (Sandbox Code Playgroud)