Довольно старый пост, но я потратил на это час или два, поэтому я хотел поделиться своим выводом, тем более что некоторые из других перечисленных комментариев не совсем правильные.
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)
б) Или это в дочерней таблице:
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
where id
= 5, она в основном выполнит
UPDATE child_table SET parent_id = NULL WHERE parent_id = 5
Я понятия не имею, зачем тебе это нужно. Я был бы удивлен, если бы многие движки баз данных даже позволили вам установить действительный внешний ключ NULL
, создавая сироту. Вроде плохая идея, но, возможно, есть вариант использования. В любом случае, если вы позволите SqlAlchemy сделать это, вы не позволите базе данных очищать дочерние элементы, используя ON DELETE CASCADE
настроенный вами. Это потому, что он полагается на эти внешние ключи, чтобы знать, какие дочерние строки следует удалить. Как только SqlAlchemy установит их все NULL
, база данных не сможет их удалить. Установка passive_deletes=True
предотвращает выдачу NULL
внешних ключей SqlAlchemy .
Вы можете узнать больше о пассивных удалениях в документации 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 о поддерживаемых каскадных функциях.