Использование унаследованного параметра отношения „backref“

Примечание

Ключевое слово relationship.backref следует считать устаревшим, и предпочтительнее использовать relationship.back_populates с явными конструкциями relationship(). Использование отдельных конструкций relationship() дает преимущества, включая то, что оба класса, сопоставленные с ORM, будут включать свои атрибуты заранее, в процессе создания класса, а не как отложенный шаг, и конфигурация будет более простой, поскольку все аргументы будут явными. Новые возможности PEP 484 в SQLAlchemy 2.0 также используют преимущества явного присутствия атрибутов в исходном коде вместо использования динамической генерации атрибутов.

См.также

Общую информацию о двунаправленных отношениях см. в следующих разделах:

Работа со связанными объектами - в Самоучитель SQLAlchemy 1.4 / 2.0, представляет обзор конфигурации двунаправленных отношений и поведения с помощью relationship.back_populates.

Управление каскадом на обратных ссылках - заметки о двунаправленном 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 declarative_base, relationship

Base = declarative_base()


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

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


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

Приведенная выше конфигурация создает коллекцию объектов Address на User под названием User.addresses. Она также устанавливает атрибут .user на Address, который будет ссылаться на родительский объект User. Используя relationship.back_populates, это эквивалентно следующему:

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

Base = declarative_base()


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

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


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

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

Поведение отношений User.addresses и Address.user заключается в том, что теперь они ведут себя двунаправленно, указывая на то, что изменения на одной стороне отношения влияют на другую. Пример и обсуждение этого поведения приведены в Самоучитель SQLAlchemy 1.4 / 2.0 в Работа со связанными объектами.

Backref Аргументы по умолчанию

Поскольку relationship.backref генерирует совершенно новый relationship(), процесс генерации по умолчанию будет пытаться включить в новый relationship() соответствующие аргументы, которые соответствуют исходным аргументам. В качестве примера ниже приведен relationship(), включающий custom join condition, который также включает ключевое слово relationship.backref:

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

Base = declarative_base()


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

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


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

Когда создается «обратная ссылка», условие 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.primaryjoin и relationship.secondaryjoin; «backref» достаточно умен, чтобы понять, что эти два аргумента также должны быть «перевернуты» при генерации противоположной стороны.

Указание аргументов обратной ссылки

Многие другие аргументы для «обратной ссылки» не являются неявными и включают такие аргументы, как relationship.lazy, relationship.remote_side, relationship.cascade и relationship.cascade_backrefs. Для этого случая мы используем функцию backref() вместо строки; она будет хранить определенный набор аргументов, которые будут переданы в новый relationship() при генерации:

# <other imports>
from sqlalchemy.orm import backref


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

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

Там, где выше, мы поместили директиву lazy="joined" только на стороне Address.user, указывая, что когда выполняется запрос к Address, автоматически должно быть сделано присоединение к сущности User, которое заполнит атрибут .user каждого возвращаемого Address. Функция backref() форматирует аргументы, которые мы ей передали, в форму, которая интерпретируется принимающей relationship() как дополнительные аргументы для применения к новому отношению, которое она создает.

Вернуться на верх