Довольно старый пост, но я потратил час или два на это, поэтому я хотел поделиться своим выводом, тем более, что некоторые другие перечисленные комментарии не совсем верны.
TL; DR
Дайте дочерней таблице чужую таблицу или измените существующую, добавив ondelete='CASCADE'
:
parent_id = db.Column(db.Integer, db.ForeignKey('parent.id', ondelete='CASCADE'))
И один из следующих соотношений:
а) Это в родительской таблице:
children = db.relationship('Child', backref='parent', passive_deletes=True)
b) Или это на дочернем столе:
parent = db.relationship('Parent', backref=backref('children', passive_deletes=True))
Подробнее
Прежде всего, несмотря на то, что говорится в принятом ответе, отношения родитель / ребенок не устанавливаются с помощью relationship
, они устанавливаются с помощью ForeignKey
. Вы можете поместить relationship
в родительский или дочерний стол, и он будет работать нормально. Хотя, как видно из дочерних таблиц, вы должны использовать функцию backref
в дополнение к ключевому аргументу.
Вариант 1 (предпочтительно)
Во-вторых, SqlAlchemy поддерживает два разных вида каскадирования. Первый и тот, который я рекомендую, встроен в вашу базу данных и обычно принимает форму ограничения на объявление внешнего ключа. В PostgreSQL это выглядит так:
CONSTRAINT child_parent_id_fkey FOREIGN KEY (parent_id)
REFERENCES parent_table(id) MATCH SIMPLE
ON DELETE CASCADE
Это означает, что при удалении записи из parent_table
все соответствующие строки в child_table
будут удалены для вас базой данных. Это быстро и надежно и, вероятно, ваш лучший выбор. Вы устанавливаете это в SqlAlchemy через ForeignKey
следующим образом (часть определения дочерней таблицы):
parent_id = db.Column(db.Integer, db.ForeignKey('parent.id', ondelete='CASCADE'))
parent = db.relationship('Parent', backref=backref('children', passive_deletes=True))
ondelete='CASCADE'
- это деталь, которая создает ON DELETE CASCADE
на столе.
Попался!
Здесь есть важное предупреждение. Обратите внимание, как у меня relationship
указано с passive_deletes=True
? Если у вас этого нет, все это не будет работать. Это потому, что по умолчанию при удалении родительской записи SqlAlchemy делает что-то действительно странное. Он устанавливает внешние ключи всех дочерних строк в NULL
. Так что если вы удалите строку из parent_table
, где id
= 5, то она в основном выполнит
UPDATE child_table SET parent_id = NULL WHERE parent_id = 5
Зачем тебе это, я понятия не имею. Я был бы удивлен, если бы многие движки баз данных даже позволили вам установить действительный внешний ключ в NULL
, создавая сироту. Похоже, плохая идея, но, возможно, есть случай использования. В любом случае, если вы позволите SqlAlchemy делать это, вы не сможете очистить базу данных, используя настроенную вами ON DELETE CASCADE
. Это потому, что он использует эти внешние ключи, чтобы знать, какие дочерние строки нужно удалить. Как только SqlAlchemy установит их все на NULL
, база данных не сможет их удалить. Установка passive_deletes=True
предотвращает использование SqlAlchemy NULL
внешних ключей.
Подробнее о пассивном удалении можно прочитать в SqlAlchemy документах .
Вариант 2
Другой способ сделать это - позволить SqlAlchemy сделать это за вас. Это устанавливается с помощью аргумента cascade
relationship
. Если у вас есть отношение, определенное в родительской таблице, оно выглядит так:
children = relationship('Child', cascade='all,delete', backref='parent')
Если отношения с ребенком, вы делаете это так:
parent = relationship('Parent', backref=backref('children', cascade='all,delete'))
Опять же, это ребенок, поэтому вам нужно вызвать метод с именем backref
и поместить туда данные каскада.
Имея это в виду, когда вы удаляете родительскую строку, SqlAlchemy фактически запускает операторы удаления, чтобы вы могли очистить дочерние строки. Скорее всего, это будет не так эффективно, как позволить этой базе данных обрабатывать вас, поэтому я не рекомендую это.
Вот SqlAlchemy документы о каскадных функциях, которые он поддерживает.