SQLAlchemy多对多关系返回重复的子项

时间:2017-11-20 12:09:45

标签: python json sqlalchemy flask-sqlalchemy

我在sqlalchemy中有3个表,我声明了多对多的关系

父:

class Parent(db.Model):
    __tablename__ = 'parent'
    id = db.Column(db.Integer, nullable=False, autoincrement=True, primary_key=True)
    name = db.Column(db.String(45))

    children = relationship('Child',
        secondary = parents_children,
        back_populates='parents')

class Child(db.Model):
    __tablename__ = 'child'

    id = db.Column(db.Integer, nullable=False, autoincrement=True, primary_key=True)
    name = db.Column(db.String(45), nullable=False)

    parents = db.relationship('Parent',
        secondary=parents_children,
        back_populates='children')

最后是1个关系表:

parents_children= db.Table('parents_children', db.metadata,
    db.Column('parent_id', db.Integer, ForeignKey('parent.id')),
    db.Column('student_id', db.Integer, ForeignKey('child.id')))

因此,一个孩子可以有一个或多个父母,一个父母可以有一个或多个孩子。

现在让我们说我想带走所有孩子并将它们作为json文件返回。我想带一个child_id,child_name和parent_id,所以我提出了一个问题:

students = db.session.query(Child.id,Child.name,Parent.id).join('parents').all()

我的json就像:

{"children":[
{"name":"child1", "id":"1", "parent_id":"1"},
{"name":"child2", "id":"2", "parent_id":"2"}]}

我的第一个问题是,如果一个孩子有一个以上的父母,我的json文件将拥有与父ID相同的子女数量,而不是这个,而是让每个孩子都有一次和json, parent_id字段,我可以有:"parent_id": ["1", "2"]

我可以创建一个循环,然后在追加一个子节点之前,检查这个子节点是否存在以及是否存在,然后在字段parent_id' s中的列表中添加parent_id。 我可以这样做,但我的查询将继续为每个parent_id为同一个孩子返回一行。我的问题如下,我可以带走所有孩子,并且每个孩子都可以接受所有的parent_id吗?

1 个答案:

答案 0 :(得分:1)

这几乎就是SQL的工作方式。但是:你可以在这里利用SQLAlchemy的ORM关系,让SQLA处理"重复数据删除"为你:

students = db.session.query(Child).\
    options(joinedload("parents", innerjoin=True)).\
    all()

将获取Child对象并在同一查询中预填充父关系。要了解显式连接和连接加载之间的区别,请阅读the zen of joined eager loadinginnerjoin=True就在那里,因为您的原始查询也执行了一次。那么你可以简单地说:

students_json = {
    "children": [
        {
            "name": s.name,
            "id": s.id,
            "parent_id": [p.id for p in s.parents]
        }
        for s in students
    ]
}

如果您担心返回行的宽度,您也可以提供合适的load_only()选项:

options(load_only("id", "name"),
        joinedload("parents", innerjoin=True).load_only("id"))