Использование унаследованного параметра отношения „backref“¶
Примечание
Ключевое слово relationship.backref следует считать устаревшим, и предпочтительнее использовать relationship.back_populates с явными конструкциями relationship(). Использование отдельных конструкций relationship() дает преимущества, включая то, что оба класса, сопоставленные с ORM, будут включать свои атрибуты заранее, в процессе создания класса, а не как отложенный шаг, и конфигурация будет более простой, поскольку все аргументы будут явными. Новые возможности PEP 484 в SQLAlchemy 2.0 также используют преимущества явного присутствия атрибутов в исходном коде вместо использования динамической генерации атрибутов.
См.также
Общую информацию о двунаправленных отношениях см. в следующих разделах:
Работа с объектами, связанными с ORM - в Унифицированный учебник по SQLAlchemy, представляет обзор конфигурации двунаправленных отношений и поведения с помощью relationship.back_populates.
Поведение каскада сохранения-обновления с двунаправленными отношениями - заметки о двунаправленном relationship() поведении относительно Session каскадного поведения.
Аргумент ключевого слова 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"))Приведенная выше конфигурация создает коллекцию объектов Address на User под названием User.addresses. Она также устанавливает атрибут .user на Address, который будет ссылаться на родительский объект 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.addresses и Address.user заключается в том, что теперь они ведут себя двунаправленно, указывая на то, что изменения на одной стороне отношения влияют на другую. Пример и обсуждение этого поведения приведены в Унифицированный учебник по SQLAlchemy в Работа с объектами, связанными с ORM.
Backref Аргументы по умолчанию¶
Поскольку relationship.backref генерирует совершенно новый relationship(), процесс генерации по умолчанию будет пытаться включить в новый relationship() соответствующие аргументы, которые соответствуют исходным аргументам. В качестве примера ниже приведен relationship(), включающий custom join condition, который также включает ключевое слово 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"))Когда создается «обратная ссылка», условие 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 = mapped_column(Integer, primary_key=True)
name = mapped_column(String)
addresses = relationship(
"Address",
backref=backref("user", lazy="joined"),
)Там, где выше, мы поместили директиву lazy="joined" только на стороне Address.user, указывая, что когда выполняется запрос к Address, автоматически должно быть сделано присоединение к сущности User, которое заполнит атрибут .user каждого возвращаемого Address. Функция backref() форматирует аргументы, которые мы ей передали, в форму, которая интерпретируется принимающей relationship() как дополнительные аргументы для применения к новому отношению, которое она создает.