我正在尝试在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'参数,提供那些列的列表 应计为包含对父项的外键引用 表
有谁知道如何正确地做到这一点?
答案 0 :(得分: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())
这样做的一个缺点是组对象没有你可以使用的整齐的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()
答案 1 :(得分:1)
解决方案是在所有foreign_keys
s上指定relationship
参数:
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])
也许考虑管理员关系在另一个方向实施&#34;一个团队有很多成员和一个管理员&#34;:
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
in the documentation的说明。当两个模型相互依赖,相互引用时,这是必要的。
答案 2 :(得分:0)
好的,我终于找到了解决这个问题的方法。多对多关系可以同时存在于同一个两个表之间的一对多关系中。
以下是代码:
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())
我仍然希望有人告诉我如何同时设置一对多和一对一的关系,所以我在这里留下我的答案并且永远不会接受它。