Verwendung des Legacy-Parameters 'backref' für Beziehungen

Hinweis

Das Schlüsselwort relationship.backref sollte als Legacy betrachtet werden, und die Verwendung von relationship.back_populates mit expliziten relationship()-Konstrukten wird bevorzugt. Die Verwendung einzelner relationship()-Konstrukte bietet Vorteile, darunter, dass beide ORM-gemappten Klassen ihre Attribute sofort beim Erstellen der Klasse enthalten, anstatt als verzögerter Schritt, und die Konfiguration ist einfacher, da alle Argumente explizit sind. Neue PEP 484-Funktionen in SQLAlchemy 2.0 nutzen ebenfalls die Tatsache aus, dass Attribute explizit im Quellcode vorhanden sind, anstatt dynamische Attributerzeugung zu verwenden.

Siehe auch

Allgemeine Informationen zu bidirektionalen Beziehungen finden Sie in den folgenden Abschnitten

Arbeiten mit ORM-verknüpften Objekten - im SQLAlchemy Unified Tutorial, wird eine Übersicht über die Konfiguration und das Verhalten bidirektionaler Beziehungen mithilfe von relationship.back_populates präsentiert.

Verhalten von save-update Cascade bei bidirektionalen Beziehungen - Hinweise zum Verhalten von bidirektionalen relationship()-Beziehungen in Bezug auf Session-Kaskadenverhalten.

relationship.back_populates

Das Schlüsselwortargument relationship.backref im Konstrukt relationship() ermöglicht die automatische Generierung einer neuen relationship(), die automatisch zur ORM-Zuordnung der zugehörigen Klasse hinzugefügt wird. Diese wird dann in eine relationship.back_populates-Konfiguration gegen die aktuell konfigurierte relationship() eingefügt, wobei beide relationship()-Konstrukte aufeinander verweisen.

Ausgehend vom folgenden Beispiel

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"))

Die obige Konfiguration erstellt eine Sammlung von Address-Objekten auf User mit dem Namen User.addresses. Sie erstellt auch ein Attribut .user auf Address, das auf das übergeordnete User-Objekt verweist. Die Verwendung von relationship.back_populates ist äquivalent zu Folgendem

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")

Das Verhalten der Beziehungen User.addresses und Address.user ist, dass sie nun **bidirektional** funktionieren, was bedeutet, dass Änderungen auf einer Seite der Beziehung die andere Seite beeinflussen. Ein Beispiel und eine Diskussion dieses Verhaltens finden Sie im SQLAlchemy Unified Tutorial unter Arbeiten mit ORM-verknüpften Objekten.

Standardargumente für Backref

Da relationship.backref eine vollständige neue relationship() generiert, versucht der Generierungsprozess standardmäßig, entsprechende Argumente in der neuen relationship() zu berücksichtigen, die den ursprünglichen Argumenten entsprechen. Als Beispiel folgt hier eine relationship(), die eine benutzerdefinierte Join-Bedingung enthält und auch den Parameter relationship.backref enthält.

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"))

Wenn der „backref“ generiert wird, wird die relationship.primaryjoin-Bedingung auch in die neue relationship() kopiert.

>>> 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 || '%%'
>>>

Weitere übertragbare Argumente sind der Parameter relationship.secondary, der sich auf eine Many-to-Many-Assoziationstabelle bezieht, sowie die „Join“-Argumente relationship.primaryjoin und relationship.secondaryjoin; „backref“ ist intelligent genug, um zu erkennen, dass diese beiden Argumente auf der gegenüberliegenden Seite auch „umgekehrt“ werden müssen.

Argumente für Backref angeben

Viele andere Argumente für einen „backref“ sind nicht implizit und umfassen Argumente wie relationship.lazy, relationship.remote_side, relationship.cascade und relationship.cascade_backrefs. In diesem Fall verwenden wir die Funktion backref() anstelle eines Strings; dies speichert eine bestimmte Menge von Argumenten, die an die neue, generierte relationship() übertragen werden.

# <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"),
    )

Wo wir oben eine lazy="joined"-Direktive nur auf der Address.user-Seite platziert haben, was bedeutet, dass bei einer Abfrage gegen Address ein Join zur User-Entität automatisch erfolgen sollte, was das Attribut .user jedes zurückgegebenen Address füllt. Die Funktion backref() hat die von uns gegebenen Argumente in ein Format gebracht, das von der empfangenden relationship() als zusätzliche Argumente interpretiert wird, die auf die von ihr erstellte neue Beziehung angewendet werden sollen.