简体   繁体   English

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

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

I'm trying to create one-to-one and one-to-many relationship at the same time in Flask-SQLAlchemy.我正在尝试在 Flask-SQLAlchemy 中同时创建一对一和一对多关系。 I want to achieve this:我想实现这个:

"A group has many members and one administrator." “一个群组有很多成员和一个管理员。”

Here is what I did:这是我所做的:

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())

However I got an error:但是我得到一个错误:

sqlalchemy.exc.AmbiguousForeignKeysError: Could not determine join condition between parent/child tables on relationship Group.members - there are multiple foreign key paths linking the tables. sqlalchemy.exc.AmbiguousForeignKeysError:无法确定关系 Group.members 上父/子表之间的连接条件 - 有多个外键路径链接表。 Specify the 'foreign_keys' argument, providing a list of those columns which should be counted as containing a foreign key reference to the parent table.指定 'foreign_keys' 参数,提供应计为包含对父表的外键引用的那些列的列表。

Does anyone know how to do that properly?有谁知道如何正确地做到这一点?

The problem you're getting comes from the fact that you've defined two links between your classes - a User has a group_id (which is a Foreign Key), and a Group has an admin (which is also defined by a Foreign Key). 您遇到的问题来自于您在类之间定义了两个链接 - 一个用户有一个group_id(一个外键),一个组有一个admin(也是一个外键定义) 。 If you remove the Foreign Key from the admin field the connection is no longer ambiguous and the relationship works. 如果从管理字段中删除外键,则连接不再模糊,并且关系有效。 This is my solution to your problem (making the link one-to-one): 这是我的问题解决方案(使链接一对一):

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())

The one drawback to this is that the group object doesn't have a neat admin member object you can just use - you have to call the function group.admin() to retrieve the administrator. 这样做的一个缺点是组对象没有你可以使用的简洁admin成员对象 - 你必须调用函数group.admin()来检索管理员。 However, the group can have many members, but only one of them can be the administrator. 但是,该组可以有许多成员,但只有其中一个可以是管理员。 Obviously there is no DB-level checking to ensure that the administrator is actually a member of the group, but you could add that check into a setter function - perhaps something like: 显然,没有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()

The solution is to specify the foreign_keys argument on all relationship s: 解决方案是在所有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])

Perhaps consider the admin relation in the other direction to implement "a group has many members and one admin": 也许考虑在另一个方向的管理关系来实现“一个组有很多成员和一个管理员”:

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)

See note on post_update in the documentation . 请参阅文档中有关post_update 的说明 It is necessary when two models are mutually dependent, referencing each other. 当两个模型相互依赖,相互引用时,这是必要的。

Ok, I found a workaround for this problem finally. 好的,我终于找到了解决这个问题的方法。 The many-to-many relationship can coexist with one-to-many relationship between the same two tables at the same time. 多对多关系可以同时与同一两个表之间的一对多关系共存。

Here is the code: 这是代码:

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())

I still want someone to tell me how to set one-to-many and one-to-one relationship at the same time, so I leave my answer here and won't accept it forever. 我仍然希望有人告诉我如何同时设置一对多和一对一的关系,所以我在这里留下我的答案,不会永远接受它。

This link solved it for me这个链接帮我解决了

most important thing is to specify foreign_keys value in the relation as well as the primary join最重要的是在关系中指定 foreign_keys 值以及主连接

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

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