Короткий ответ
back_populates связывает два атрибута relationship на обеих сторонах связи, чтобы изменение одной стороны в памяти Python автоматически синхронизировало другую; cascade="all, delete-orphan" на стороне "один" означает, что при удалении родителя все связанные дочерние объекты будут автоматически удалены СУБД, а дочерний объект, у которого убрали ссылку на родителя, будет считаться «осиротевшим» и также удалён при flush.
Как это работает подробнее
back_populates — это Python-уровневая синхронизация объектов в памяти сессии (в отличие от backref, который создаёт обратный атрибут автоматически); cascade управляет тем, какие операции SQLAlchemy ORM применяет к связанным объектам при соответствующей операции с родителем.
Пример кода
example.py
class Author(Base):
__tablename__ = "authors"
id: Mapped[int] = mapped_column(primary_key=True)
books: Mapped[list["Book"]] = relationship(back_populates="author", cascade="all, delete-orphan")
class Book(Base):
__tablename__ = "books"
id: Mapped[int] = mapped_column(primary_key=True)
author_id: Mapped[int] = mapped_column(ForeignKey("authors.id"))
author: Mapped["Author"] = relationship(back_populates="books")
# session.delete(author); session.commit() -> удалит author и все его books автоматическиЧто использовать на практике
- Практика. cascade="all, delete-orphan" типичен для составных сущностей, где дочерний объект не имеет смысла без родителя; важно не путать ORM-уровень cascade (срабатывает только через сессию) с ON DELETE CASCADE на уровне БД (срабатывает при прямом SQL в обход ORM).
Подводные камни
- Ошибка. Настраивают только cascade на уровне ORM без ON DELETE на уровне FK в БД, и прямое удаление через raw SQL оставляет «осиротевшие» строки; используют одновременно backref и back_populates на той же паре атрибутов, вызывая конфликт конфигурации.