Использование унаследованного параметра отношения „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()
как дополнительные аргументы для применения к новому отношению, которое она создает.