使用旧式的 ‘backref’ 关系参数

注意

应将 relationship.backref 关键字视为旧式用法,并且应优先使用 relationship.back_populates 和显式的 relationship() 构造。使用单独的 relationship() 构造具有诸多优势,包括两个 ORM 映射类都将在类构造时预先包含其属性,而不是作为延迟步骤,并且配置更直接,因为所有参数都是显式的。SQLAlchemy 2.0 中的新 PEP 484 功能也利用了源代码中显式存在的属性,而不是使用动态属性生成。

另请参阅

有关双向关系的常规信息,请参阅以下章节

使用 ORM 相关对象 - 在 SQLAlchemy 统一教程 中,概述了使用 relationship.back_populates 的双向关系配置和行为

使用双向关系进行保存-更新级联的行为 - 关于 relationship()Session 级联行为方面的双向行为的注释。

relationship.back_populates

relationship.backref 关键字参数在 relationship() 构造上允许自动生成一个新的 relationship(),该关系将自动添加到相关类的 ORM 映射中。然后,它将被放置到针对当前正在配置的 relationship()relationship.back_populates 配置中,并且两个 relationship() 构造相互引用。

从以下示例开始

from sqlalchemy import Column, ForeignKey, Integer, String
from sqlalchemy.orm import DeclarativeBase, relationship


class Base(DeclarativeBase):
    pass


class User(Base):
    __tablename__ = "user"
    id = mapped_column(Integer, primary_key=True)
    name = mapped_column(String)

    addresses = relationship("Address", backref="user")


class Address(Base):
    __tablename__ = "address"
    id = mapped_column(Integer, primary_key=True)
    email = mapped_column(String)
    user_id = mapped_column(Integer, ForeignKey("user.id"))

上述配置在 User 上建立了一个名为 User.addressesAddress 对象集合。它还在 Address 上建立了一个 .user 属性,该属性将引用父 User 对象。使用 relationship.back_populates,它等效于以下内容

from sqlalchemy import Column, ForeignKey, Integer, String
from sqlalchemy.orm import DeclarativeBase, relationship


class Base(DeclarativeBase):
    pass


class User(Base):
    __tablename__ = "user"
    id = mapped_column(Integer, primary_key=True)
    name = mapped_column(String)

    addresses = relationship("Address", back_populates="user")


class Address(Base):
    __tablename__ = "address"
    id = mapped_column(Integer, primary_key=True)
    email = mapped_column(String)
    user_id = mapped_column(Integer, ForeignKey("user.id"))

    user = relationship("User", back_populates="addresses")

User.addressesAddress.user 关系的行为是,它们现在以双向方式运行,表明关系一侧的更改会影响另一侧。有关此行为的示例和讨论,请参见 SQLAlchemy 统一教程 中的 使用 ORM 相关对象

Backref 默认参数

由于 relationship.backref 生成了一个全新的 relationship(),因此默认情况下,生成过程将尝试在新 relationship() 中包含与原始参数相对应的参数。例如,下面是一个 relationship(),其中包含一个 自定义连接条件,该条件还包括 relationship.backref 关键字

from sqlalchemy import Column, ForeignKey, Integer, String
from sqlalchemy.orm import DeclarativeBase, relationship


class Base(DeclarativeBase):
    pass


class User(Base):
    __tablename__ = "user"
    id = mapped_column(Integer, primary_key=True)
    name = mapped_column(String)

    addresses = relationship(
        "Address",
        primaryjoin=(
            "and_(User.id==Address.user_id, Address.email.startswith('tony'))"
        ),
        backref="user",
    )


class Address(Base):
    __tablename__ = "address"
    id = mapped_column(Integer, primary_key=True)
    email = mapped_column(String)
    user_id = mapped_column(Integer, ForeignKey("user.id"))

生成 “backref” 时,relationship.primaryjoin 条件也会被复制到新的 relationship()

>>> print(User.addresses.property.primaryjoin)
"user".id = address.user_id AND address.email LIKE :email_1 || '%%'
>>>
>>> print(Address.user.property.primaryjoin)
"user".id = address.user_id AND address.email LIKE :email_1 || '%%'
>>>

其他可传递的参数包括引用多对多关联表的 relationship.secondary 参数,以及 “join” 参数 relationship.primaryjoinrelationship.secondaryjoin;“backref” 足够智能,知道在生成另一侧时,这两个参数也应该被 “反转”。

指定 Backref 参数

许多其他 “backref” 的参数不是隐式的,包括 relationship.lazy, relationship.remote_side, relationship.cascaderelationship.cascade_backrefs 等参数。对于这种情况,我们使用 backref() 函数代替字符串;这将存储一组特定的参数,这些参数将在生成新的 relationship() 时传递给它

# <other imports>
from sqlalchemy.orm import backref


class User(Base):
    __tablename__ = "user"
    id = mapped_column(Integer, primary_key=True)
    name = mapped_column(String)

    addresses = relationship(
        "Address",
        backref=backref("user", lazy="joined"),
    )

在上面,我们仅在 Address.user 侧放置了 lazy="joined" 指令,表明当对 Address 进行查询时,应自动进行与 User 实体的连接,这将填充每个返回的 Address.user 属性。backref() 函数将我们给它的参数格式化为接收 relationship() 解释的形式,作为应用于其创建的新关系的附加参数。