Dość stary post, ale spędziłem nad tym godzinę lub dwie, więc chciałem podzielić się moim odkryciem, zwłaszcza, że niektóre inne wymienione komentarze nie są całkiem poprawne.
TL; DR
Nadaj tabeli potomnej obcą tabelę lub zmodyfikuj istniejącą, dodając ondelete='CASCADE'
:
parent_id = db.Column(db.Integer, db.ForeignKey('parent.id', ondelete='CASCADE'))
I jedną z następujących relacji:
a) To na stole nadrzędnym:
children = db.relationship('Child', backref='parent', passive_deletes=True)
b) Lub to na stole podrzędnym:
parent = db.relationship('Parent', backref=backref('children', passive_deletes=True))
Detale
Po pierwsze, pomimo tego, co mówi zaakceptowana odpowiedź, relacja rodzic / dziecko nie jest ustanawiana przez używanie relationship
, lecz przez używanie ForeignKey
. Możesz umieścić relationship
tabelę nadrzędną lub podrzędną i będzie działać dobrze. Chociaż najwyraźniej w tabelach podrzędnych musisz użyć backref
funkcji oprócz argumentu słowa kluczowego.
Opcja 1 (preferowana)
Po drugie, SqlAlchemy obsługuje dwa różne rodzaje kaskadowania. Pierwsza i ta, którą polecam, jest wbudowana w twoją bazę danych i zwykle ma formę ograniczenia deklaracji klucza obcego. W PostgreSQL wygląda to tak:
CONSTRAINT child_parent_id_fkey FOREIGN KEY (parent_id)
REFERENCES parent_table(id) MATCH SIMPLE
ON DELETE CASCADE
Oznacza to, że jeśli usuniesz rekord z parent_table
, wszystkie odpowiadające mu wiersze child_table
zostaną usunięte przez bazę danych. Jest szybki i niezawodny i prawdopodobnie najlepszym rozwiązaniem. Konfigurujesz to w SqlAlchemy w ForeignKey
następujący sposób (część definicji tabeli podrzędnej):
parent_id = db.Column(db.Integer, db.ForeignKey('parent.id', ondelete='CASCADE'))
parent = db.relationship('Parent', backref=backref('children', passive_deletes=True))
Jest ondelete='CASCADE'
to część, która tworzy ON DELETE CASCADE
na stole.
Mam cię!
Tutaj jest ważne zastrzeżenie. Zauważ, jak mam relationship
określone z passive_deletes=True
? Jeśli tego nie masz, całość nie będzie działać. Dzieje się tak, ponieważ domyślnie podczas usuwania rekordu nadrzędnego SqlAlchemy robi coś naprawdę dziwnego. Ustawia klucze obce wszystkich wierszy podrzędnych na NULL
. Więc jeśli usuniesz wiersz, z parent_table
którego id
= 5, to w zasadzie zostanie wykonany
UPDATE child_table SET parent_id = NULL WHERE parent_id = 5
Dlaczego tego chcesz, nie mam pojęcia. Zdziwiłbym się, gdyby wiele silników baz danych pozwoliło nawet ustawić prawidłowy klucz obcy na NULL
, tworząc sierotę. Wydaje się, że to zły pomysł, ale może jest przypadek użycia. W każdym razie, jeśli pozwolisz SqlAlchemy to zrobić, uniemożliwisz bazie danych wyczyszczenie elementów podrzędnych za pomocą skonfigurowanego przez Ciebie pliku ON DELETE CASCADE
. Dzieje się tak, ponieważ opiera się na tych kluczach obcych, aby wiedzieć, które wiersze potomne należy usunąć. Gdy SqlAlchemy ustawi je wszystkie na NULL
, baza danych nie może ich usunąć. Ustawienie passive_deletes=True
uniemożliwia SqlAlchemy NULL
wyodrębnianie kluczy obcych.
Możesz przeczytać więcej o pasywnym usuwaniu w dokumentacji SqlAlchemy .
Opcja 2
Innym sposobem, w jaki możesz to zrobić, jest pozwolenie SqlAlchemy zrobić to za Ciebie. Jest to konfigurowane za pomocą cascade
argumentu relationship
. Jeśli masz relację zdefiniowaną w tabeli nadrzędnej, wygląda to tak:
children = relationship('Child', cascade='all,delete', backref='parent')
Jeśli związek dotyczy dziecka, robisz to w ten sposób:
parent = relationship('Parent', backref=backref('children', cascade='all,delete'))
Ponownie, jest to dziecko, więc musisz wywołać wywołaną metodę backref
i umieścić tam dane kaskady.
Dzięki temu po usunięciu wiersza nadrzędnego SqlAlchemy faktycznie uruchomi instrukcje usuwania, aby wyczyścić wiersze podrzędne. Prawdopodobnie nie będzie to tak wydajne, jak pozwolenie tej bazie danych na obsługę, jeśli więc nie polecam tego.
Oto dokumenty SqlAlchemy dotyczące obsługiwanych funkcji kaskadowych.