Досить старий пост, але я просто витратив на це годину-дві, тому я хотів поділитися своїм висновком, тим більше, що деякі з інших перерахованих коментарів не зовсім правильні.
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
звідки 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 про каскадні функції, які він підтримує.