简体   繁体   English

SQLAlchemy 用户多对多关系

[英]SQLAlchemy User Many to Many Relationship

I am working on a user structure where a user can have parents and children that consist of user objects.我正在研究一个用户结构,其中用户可以拥有由用户对象组成的父母和孩子。 I have been trying to get the following to work in multiple different ways in SQLAlchemy and Flask.我一直在尝试让以下内容在 SQLAlchemy 和 Flask 中以多种不同的方式工作。

Here is an example of how I want to structure this:这是我要如何构建它的示例:

UserTable
id | name
---+-----
1  | Kim
2  | Tammy
3  | John
4  | Casey
5  | Kyle

UserRelationship
id | parent_user_id | child_user_id
---+----------------+---------------
1  | 1              | 2
2  | 1              | 3
3  | 4              | 2

Where Kim is the parent to Tammy and John. Kim 是 Tammy 和 John 的父母。 Casey is the parent of Tammy.凯西是塔米的父母。 Tammy is the child of Kim and Casey.塔米是金和凯西的孩子。 John is the child of Kim.约翰是金的孩子。 Kyle has no children nor parents.凯尔没有孩子也没有父母。


My error reads:我的错误是:

sqlalchemy.exc.AmbiguousForeignKeysError: Could not determine join condition 
between parent/child tables on relationship User.parents - there are multiple 
foreign key paths linking the tables via secondary table 'user_relationship'. 
Specify the 'foreign_keys' argument, providing a list of those columns which 
should be counted as containing a foreign key reference from the secondary 
table to each of the parent and child tables.

My model.py file looks like:我的model.py文件如下所示:

user_relationship = db.Table(
    'user_relationship',
    db.Model.metadata,
    db.Column('child_user_id', db.Integer, db.ForeignKey('user.id')),
    db.Column('parent_user_id', db.Integer, db.ForeignKey('user.id'))
)

class User(db.Model):
    __tablename__ = 'user'

    id = db.Column(db.Integer, primary_key=True, unique=True, nullable=False)
    email = db.Column(db.String(120), unique=True)
    pwdhash = db.Column(db.String(54))

    parents = relationship('User',
        secondary=user_relationship,
        backref=db.backref('children'),
        cascade="all,delete")

This might not be the best way to handle many to many hierarchical user relationships in Flask or SQLAlchemy. Any insight on how to build this out would be great.这可能不是处理 Flask 或 SQLAlchemy 中多对多分层用户关系的最佳方式。任何关于如何构建它的见解都会很棒。

Thank you谢谢

db.Table is mainly used when there is a Many to Many relation between two different entities. db.Table主要用于两个不同实体之间存在Many to Many关系时。 Here both parent and child are User s. 这里父母和孩子都是User

In your case, below code would be fine: 就您而言,下面的代码会很好:

class User(db.Model):
    __tablename__ = 'users'
    id = db.Column(db.Integer, primary_key=True, unique=True,     nullable=False)
    email = db.Column(db.String(120), unique=True)
    pwdhash = db.Column(db.String(54))


class Parent(db.Model):
    __tablename__ = 'parents'
    child_id = db.Column(db.Integer, db.Foreignkey('users.id'))
    parent_id = db.Column(db.Integer, db.Foreignkey('users.id'))
    child = db.relationship('User', foreign_keys=[child_id], backref = 'parents')

flask_sqlalchemy is written on top of SQLAlchemy , and there is a nice elaboration of this issue here (Handling Multiple Join Paths) in SQLAlchemy's website. flask_sqlalchemy写在上面SQLAlchemy ,有这个问题的一个很好的阐述在这里 (处理多个连接路径)在SQLAlchemy的网站。

Use primaryjoin and secondaryjoin args.使用primaryjoinsecondaryjoin参数。

Recently I faced similar issue, although I see documentation and accepted answer s recommend to user Parent model instead of creating a relationship table, it didn't make sense for me to create a separate model for just storing a relationship since my all models inherit from BaseModel and I don't want this relationship to get BaseModel 's additional features.最近我遇到了类似的问题,虽然我看到文档和接受的答案建议用户Parent model 而不是创建关系表,但我创建一个单独的 model 来存储关系是没有意义的,因为我的所有模型都继承自BaseModel和我不希望这种关系获得BaseModel的附加功能。 Mainly because my BaseModel has id by default and BaseTable does not contain id主要是因为我的BaseModel默认有idBaseTable没有包含id

So, I prefer creating a separate Sqlalchemy Table for relationship instead of Model.所以,我更喜欢为关系创建一个单独的 Sqlalchemy 表,而不是 Model。

To accomplish it you may use primaryjoin and secondaryjoin arguments as following.要完成它,您可以使用primaryjoinsecondaryjoin arguments,如下所示。 I have many-to-one relationship of investors with relationship_manager.我与 relationship_manager 之间存在多对一的投资者关系。 To make it many-to-many you may remove unique=True from investor_id in relation table and remove uselist=False from relationship_manager in User model.要使其成为多对多,您可以从关系表中的investor_id中删除unique=True ,并从用户 model 中的 relationship_manager 中删除uselist=False

# user.py 
from database.relationships.relationship_managers import relationship_managers

class User(BaseModel):
    id = Column(Integer, primary_key=True)
    relationship_manager = relationship(
        "User",
        secondary=relationship_managers,
        uselist=False,
        primaryjoin=("relationship_managers.c.investor_id == User.id"),
        secondaryjoin=("relationship_managers.c.rel_manager_id == User.id"),
        backref="investors"
    )
# relationship_managers.py
relationship_managers = BaseTable(
    "relationship_managers",
    Column("investor_id", Integer, ForeignKey('user.id', ondelete="CASCADE"), unique=True),
    Column("rel_manager_id", Integer, ForeignKey('user.id', ondelete="CASCADE")),
)

Here, primaryjoin performs the first join with investor_id and gets the record User record, but this is not sufficient for us.在这里, primaryjoin执行第一次与 investor_id 的连接并获取记录用户记录,但这对我们来说还不够。 So, we need secondaryjoin to specifically get the relationship_manager's id.所以,我们需要secondaryjoin专门获取 relationship_manager 的 id。

Feel free to comment and suggest if any doubts.如有任何疑问,请随时发表评论并提出建议。 Hopefully this helps anyone who has Models and Tables both in their Project using Sqlalchemy. :)希望这可以帮助使用 Sqlalchemy 在其项目中同时拥有模型和表格的任何人。:)

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

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