使用遗留的“backref”关系参数

注意

relationship.backref 关键字应被视为遗留,建议使用 relationship.back_populates 和明确的 relationship() 结构。使用单独的 relationship() 结构有以下优势:ORM 映射类将在构造时立即包含其属性,而不是作为延迟步骤,并且配置更直观,因为所有参数都是明确的。SQLAlchemy 2.0 中的新 PEP 484 功能也利用了属性在源代码中明确存在,而不是使用动态属性生成。

另请参阅

有关双向关系的一般信息,请参阅以下部分

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

双向关系中 save-update 级联的行为 - 关于双向 relationship() 行为的说明,涉及 Session 级联行为。

relationship.back_populates

relationship.backref 关键字参数在 relationship() 结构中允许自动生成一个新的 relationship(),它将自动添加到相关类的 ORM 映射中。然后,它将被放置在 relationship.back_populates 配置中,指向当前正在配置的 relationship(),两个 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 参数,它引用多对多关联表,以及“连接”参数 relationship.primaryjoinrelationship.secondaryjoin;“backref”足够智能,知道在生成相反方向时,这两个参数也应该“反转”。

指定 Backref 参数

许多其他“backref”参数并非隐式,包括 relationship.lazyrelationship.remote_siderelationship.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() 可以解释的形式,作为要应用于其创建的新关系的附加参数。