【问题标题】:When do I need to use sqlalchemy back_populates?我什么时候需要使用 sqlalchemy back_populates?
【发布时间】:2021-09-04 04:27:59
【问题描述】:

当我按照本指南尝试 SQLAlchemy 关系示例时:Basic Relationship Patterns

我有这个代码

#!/usr/bin/env python
# encoding: utf-8
from sqlalchemy import create_engine
from sqlalchemy import Table, Column, Integer, ForeignKey
from sqlalchemy.orm import relationship, sessionmaker
from sqlalchemy.ext.declarative import declarative_base

engine = create_engine('sqlite:///:memory:', echo=True)
Session = sessionmaker(bind=engine)
session = Session()
Base = declarative_base(bind=engine)

class Parent(Base):
    __tablename__ = 'parent'
    id = Column(Integer, primary_key=True)
    children = relationship("Child")

class Child(Base):
    __tablename__ = 'child'
    id = Column(Integer, primary_key=True)
    parent_id = Column(Integer, ForeignKey('parent.id'))
    parent = relationship("Parent")

Base.metadata.create_all()

p = Parent()
session.add(p)
session.commit()
c = Child(parent_id=p.id)
session.add(c)
session.commit()
print "children: {}".format(p.children[0].id)
print "parent: {}".format(c.parent.id)

效果很好,但在指南中,它说模型应该是:

class Parent(Base):
    __tablename__ = 'parent'
    id = Column(Integer, primary_key=True)
    **children = relationship("Child", back_populates="parent")**

class Child(Base):
    __tablename__ = 'child'
    id = Column(Integer, primary_key=True)
    parent_id = Column(Integer, ForeignKey('parent.id'))
    **parent = relationship("Parent", back_populates="children")**

为什么在我的示例中不需要back_populatesbackref?我应该什么时候使用其中一种?

【问题讨论】:

    标签: python sqlalchemy


    【解决方案1】:

    如果使用backref,则不需要在第二个表上声明关系。

    class Parent(Base):
        __tablename__ = 'parent'
        id = Column(Integer, primary_key=True)
        children = relationship("Child", backref="parent")
    
    class Child(Base):
        __tablename__ = 'child'
        id = Column(Integer, primary_key=True)
        parent_id = Column(Integer, ForeignKey('parent.id'))
    

    如果您使用backref,并分别定义relationship,那么如果您不使用back_populates,sqlalchemy 将不知道连接关系,因此修改一个也会修改另一个。

    因此,在您的示例中,您单独定义了 relationship,但未提供 back_populates 参数,修改一个字段不会自动更新事务中的另一个字段。

    >>> parent = Parent()
    >>> child = Child()
    >>> child.parent = parent
    >>> print(parent.children)
    []
    

    看看它是如何没有自动填写children 字段的?

    现在,如果您提供 back_populates 参数,sqlalchemy 将连接这些字段。

    class Parent(Base):
        __tablename__ = 'parent'
        id = Column(Integer, primary_key=True)
        children = relationship("Child", back_populates="parent")
    
    class Child(Base):
        __tablename__ = 'child'
        id = Column(Integer, primary_key=True)
        parent_id = Column(Integer, ForeignKey('parent.id'))
        parent = relationship("Parent", back_populates="children")
    

    所以现在我们得到

    >>> parent = Parent()
    >>> child = Child()
    >>> child.parent = parent
    >>> print(parent.children)
    [Child(...)]
    

    Sqlalchemy 现在知道这两个字段是相关的,并且会随着另一个字段的更新而更新。值得注意的是,使用backref 也可以做到这一点。如果您想在每个类上定义关系,使用back_populates 非常好,因此很容易看到所有字段只是扫视模型类,而不必查看通过 backref 定义字段的其他类。

    【讨论】:

    • A note about back_populates vs backref: backref 更简洁,因为您不需要在两个类上声明关系,但实际上我发现不值得保存它在线的。我认为back_populates 更好,不仅因为在python 文化中“显式优于隐式”(Python 之禅),而且当你有很多模型时,快速浏览一下它的声明就可以查看所有关系及其名称,而不是查看所有相关模型。此外,back_populates 的另一个好处是您可以在大多数 IDE 上在两个方向上自动完成 =)
    • backref 可能看起来更容易实现(特别是如果您使用 cmets 清楚地指示两个类中的关系,至少我是这么认为的......),直到您需要对单个表实现多个关系并且使用容器。最后 back_populates 让你的代码更容易理解
    • 在 Child 下真的需要 parent_id 吗?那么帮助表呢,如in the documentation 所示
    • @LuizTauffer parent_id 是存储父子关系的实际外键字段。有必要。 Helper 表用于定义多对多关系(例如,如果 Child 可以有多个 Parent)。上面的 fkey 示例是经典的一对多示例,其中每个 Child 有一个且只有一个 Parent,并且一个 parent 可以有多个 Child。
    • 如果您只需要从一侧检索信息,您可以仅在给定一侧添加 back_populates。例如,您可以在子节点上设置 back_populates,这将提供有关父节点的信息,并让父类不带有子节点引用的“back_populated”
    猜你喜欢
    • 1970-01-01
    • 2013-03-17
    • 1970-01-01
    • 1970-01-01
    • 2015-05-26
    • 1970-01-01
    • 2010-09-21
    • 2018-02-25
    相关资源
    最近更新 更多