Насколько я понимаю из документации, первый случай используется по умолчанию, поскольку безопаснее всего не предполагать ничего о каскадной конфигурации бэкэнда (а некоторые бэкэнды имеют ограниченную/необычную/отсутствующую поддержку этой функции). Последнее позволяет оптимизировать и/или использовать более сложные сценарии, например, перекладывая «далеко идущие» (как по объему затронутых строк, так и по глубине каскадирования/рекурсии) каскады на сторону базы данных. В этом случае при удалении «родительской» строки ORM выдает только явные инструкции DELETE для этой строки и любых объектов, которые она «загрузила», но полагается на БД для обработки любых других каскадов.
Существует третий вариант: пассивный_deletes="all". Мне не совсем понятно, каков предполагаемый вариант использования этой опции, хотя из того, что я смог собрать (и проверить в ходе тестирования), кажется, что ORM not выдает DELETE для дочерних элементов (загруженных или нет) при удалении родителя. Результирующий SQL-запрос такой же, как если бы пассивный_deletes не был указан (по умолчанию = False) AND cascade="{...}, delete" также опущен (т. е. ORM "наивен" для конфигурации БД) AND {выполнены некоторые другие условия - см. пример [A]}. В сценарии, где ON DELETE CASCADE указан (как известно) во внутренней БД, результирующее состояние БД (после удаления) также идентично в этих случаях, хотя я инстинктивно подскажу, что явно объявленное каскадное поведение требуется, чтобы ORM «знал» о каскадных удалениях - хотя я не мог создать тестовый сценарий, в котором это «имело бы значение» (т. е. я не мог принудительно вызвать исключение или сгенерировать некоторые противоречивый результат).
Я построил несколько тестовых примеров с различными комбинациями параметров и наблюдал как выдаваемый SQL, так и достигнутую БД. Я не буду включать все это, так как результаты имеют некоторую избыточность, но ключевые примеры таковы (некоторые шаблоны SQLAlchemy опущены согласно их документации)
Инициализируйте БД с помощью чистого SQL — я использую PostgreSQL.
Код: Выделить всё
CREATE TABLE IF NOT EXISTS parent (
id int PRIMARY KEY
);
CREATE TABLE IF NOT EXISTS child (
id int PRIMARY KEY,
parent_id int REFERENCES parent ON DELETE CASCADE
);
INSERT INTO parent VALUES (1);
INSERT INTO child VALUES (1, 1);
INSERT INTO child VALUES (2, 1);
INSERT INTO child VALUES (3, 1);
Код: Выделить всё
class Parent(Base):
__tablename__ = "parent"
id: Mapped[int] = mapped_column(primary_key=True)
class Child(Base):
__tablename__ = "child"
id: Mapped[int] = mapped_column(primary_key=True)
parent_id: Mapped[int] = mapped_column(ForeignKey("parent.id"))
p1 = session.get(Parent, 1)
session.delete(p1)
session.commit()
Случай B — добавление отношения()
Код: Выделить всё
class Parent(Base):
__tablename__ = "parent"
id: Mapped[int] = mapped_column(primary_key=True)
children: Mapped[list["Child"]] = relationship()
# (... as above)
Случай C — добавьте cascade="all, delete" в Relations()
Код: Выделить всё
# (...)
children: Mapped[list["Child"]] = relationship(cascade="all, delete")
# (...)
Случай D — добавьте пассивный_deletes=True
Код: Выделить всё
# (...)
children: Mapped[list["Child"]] = relationship(
cascade="all, delete", passive_deletes=True)
# (...)
Случай E — «Загрузить» дочерний элемент перед удалением
Код: Выделить всё
# (...)
p1 = session.get(Parent, 1)
c2 = p1.children[1]
session.delete(p1)
# (...)
Случай F – измените пассивный_deletes=True на пассивный_deletes="all"
Это вызывает исключение в строке p1 = session.get(Parent, 1), чтение "не может быть установлено пассивный_deletes='all' в сочетании с каскадом 'delete' или 'delete-orphan'"; Означает ли это, что использование пассивного_deletes='all' предназначено для случаев, когда БД (и только БД) обрабатывает каскадирование, и поэтому его нельзя настраивать в Relations()?
Случай G - удалите cascade="all, delete" из Relations()
Теперь выдаваемый SQL соответствует A (и D), хотя я не знаю, как проверить «осведомленность» ORM в каждом из этих трех случаев, чтобы определить различия (и, следовательно, вариант использования/лучшие практики в отношении пассивного_deletes="all").
Некоторые примечания/предостережения
[*]Я нашел этот комментарий, который может указывать на то, что он устарел/устарел: https://github.com/sqlalchemy/sqlalchem ... -441935264.
[*]В документации говорится, что это «отключает «обнуление» дочерних внешних ключей», и что «это очень особая настройка варианта использования», хотя я не могу ее придумать
Подробнее здесь: https://stackoverflow.com/questions/785 ... in-e-g-pos
Мобильная версия