【问题标题】:Flask and SQLalchemy append relationship without loading objects every timeFlask 和 SQLalchemy 追加关系,无需每次都加载对象
【发布时间】:2018-01-18 11:34:43
【问题描述】:

我正在休息 api 与 python 烧瓶和 SQLalchemy。我有 2 个类 Parent 和 Child:

Class Parent(db.Model):
    id = db.Column(db.Integer, nullable=False, autoincrement=True, primary_key=True)
    name = db.Column(db.String, nullable=False)

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


Class Child(db.Model):
    id = db.Column(db.Integer, nullable=False, autoincrement=True, primary_key=True)
    name = db.Column(db.String, nullable=False)

    parents = relationship('Parent',
                                secondary=parent_has_children,
                                back_populates='children'
                                )

   parent_has_children = db.Table('parent_has_children', db.metadata,
                            db.Column('parent_id', db.Integer, ForeignKey('Parent.id')),
                            db.Column('child_id', db.Integer, ForeignKey('Child.id'))
                            )

我有一个多对多的关系,因此我使用辅助表。假设我有一个接收 child_id 和 parent_id 并建立它们的关系的路由:

@app.route('/buildrelationship', methods=['POST'])
def buildrelationship():
    child_id= request.json['student_id']
    parent_id = request.json['parent_id']
    child = Child.query.get(child_id)
    parent = Parent.query.get(parent_id)
    parent.children.append(child)
    db.session.commit()

这样我添加了父子之间的关系,但我必须先从数据库中获取父子关系,然后再添加关系。 request.json 可能有一个要附加到父级的子级列表或要附加到特定子级的父级列表。在这种情况下,我必须查询与列表长度一样多的次数才能获取父级或子级,然后追加关系。有没有更好的方法来追加关系而不是每次都加载父子对象?

【问题讨论】:

    标签: python sqlalchemy


    【解决方案1】:

    可以减少查询,但您希望尽可能多地依赖 ORM。如果您没有执行查询来将 POSTed 数据解析为子对象或父对象,而是直接将 id 插入到 M2M 表中 - 您可能会对数据库的完整性感到头疼。

    您只需要触发一个更新查询——如果您首先遍历您的子列表一次,您最终可能会得到一个children = [Child<1>, Child<2>, Child<3>] 列表,那么您可以只使用Parent.children.append(children)

    如果您真的在每个 POST 处理数万/数十万个子对象并且时间、内存等实际上是一个问题,您可以转向批量加载数据,几乎完全跳过 ORM 层和所有它可以帮助您的安全功能。

    首先,您需要获取当前子对象的列表,这样您就可以确保不会导致完整性错误(同样,安全手套已经摘下,如果您这样做,您会很奇怪这没有充分的理由)。

    existing = {x.id for x in Child.query.all()}
    # lets say: existing = {1,3,4,5,6}
    

    接下来,您将获得已发布的子 ID 列表:

    target = request.json['student_id']
    # lets say target = [1,2,3,3,3]
    

    因此,我们现在可以过滤出实际需要插入的内容,清除任何可能给我们带来麻烦的内容:

    children_to_insert = {x for x in target if x in existing}
    # active_children = {1,3}
    

    建立一个字典列表来表示我们的 M2M 表数据:

    parent_id = request.json['parent_id'] 
    bulk = [{'parent_id': parent_id, 'child_id': x} for x in children_to_insert]
    
    # Then we'd bulk operation it into the database:
    
    db.engine.execute(
        parent_has_children.insert(bulk)
    )
    

    我已经跳过了你想要的所有其他完整性检查(父级存在吗?它已经有子级了吗?)但你希望能明白这一点,也就是说,只需使用 ORM,不要试图四处走动它回来了,没有很好的理由。

    【讨论】:

    • 如果设置正确,他们还可以依赖 RDBMS 及其约束,在这种情况下,您展示的批量方法同样安全。
    • 当然,尽管很多人在使用 SQLite3 时并没有意识到他们需要打开一个选项来获得他们可能期望内置的 FK 完整性控制。
    • 这也是真的。
    猜你喜欢
    • 2014-03-27
    • 2014-10-13
    • 2021-08-31
    • 1970-01-01
    • 1970-01-01
    • 2018-08-22
    • 2021-04-17
    • 2022-09-29
    • 1970-01-01
    相关资源
    最近更新 更多