Pos yang cukup lama, tetapi saya hanya menghabiskan satu atau dua jam untuk ini, jadi saya ingin membagikan temuan saya, terutama karena beberapa komentar lain yang tercantum kurang tepat.
TL; DR
Berikan tabel anak asing atau modifikasi yang sudah ada, dengan menambahkan ondelete='CASCADE'
:
parent_id = db.Column(db.Integer, db.ForeignKey('parent.id', ondelete='CASCADE'))
Dan salah satu dari hubungan berikut:
a) Ini di tabel induk:
children = db.relationship('Child', backref='parent', passive_deletes=True)
b) Atau ini di meja anak:
parent = db.relationship('Parent', backref=backref('children', passive_deletes=True))
Detail
Pertama, terlepas dari apa yang dikatakan jawaban yang diterima, hubungan orang tua / anak tidak dibuat dengan menggunakan relationship
, itu dibuat dengan menggunakan ForeignKey
. Anda dapat meletakkannya relationship
di tabel induk atau anak dan itu akan bekerja dengan baik. Meskipun, tampaknya pada tabel anak, Anda harus menggunakan backref
fungsi selain argumen kata kunci.
Opsi 1 (lebih disukai)
Kedua, SqlAlchemy mendukung dua jenis cascading. Yang pertama, dan yang saya rekomendasikan, dibangun ke dalam database Anda dan biasanya berbentuk batasan pada deklarasi kunci asing. Di PostgreSQL terlihat seperti ini:
CONSTRAINT child_parent_id_fkey FOREIGN KEY (parent_id)
REFERENCES parent_table(id) MATCH SIMPLE
ON DELETE CASCADE
Ini berarti bahwa ketika Anda menghapus record dari parent_table
, maka semua baris terkait child_table
akan dihapus untuk Anda oleh database. Ini cepat dan andal dan mungkin taruhan terbaik Anda. Anda mengatur ini di SqlAlchemy melalui ForeignKey
seperti ini (bagian dari definisi tabel anak):
parent_id = db.Column(db.Integer, db.ForeignKey('parent.id', ondelete='CASCADE'))
parent = db.relationship('Parent', backref=backref('children', passive_deletes=True))
Ini ondelete='CASCADE'
adalah bagian yang membuat di ON DELETE CASCADE
atas meja.
Kena kau!
Ada peringatan penting di sini. Perhatikan bagaimana saya relationship
menentukan dengan passive_deletes=True
? Jika Anda tidak memilikinya, semuanya tidak akan berfungsi. Ini karena secara default ketika Anda menghapus catatan induk SqlAlchemy melakukan sesuatu yang sangat aneh. Ini menetapkan kunci asing dari semua baris anak ke NULL
. Jadi jika Anda menghapus baris dari parent_table
where id
= 5, maka pada dasarnya itu akan dijalankan
UPDATE child_table SET parent_id = NULL WHERE parent_id = 5
Mengapa Anda menginginkan ini, saya tidak tahu. Saya akan terkejut jika banyak mesin database bahkan mengizinkan Anda untuk mengatur kunci asing yang valid NULL
, membuat yatim piatu. Sepertinya ide yang buruk, tapi mungkin ada kasus penggunaan. Bagaimanapun, jika Anda membiarkan SqlAlchemy melakukan ini, Anda akan mencegah database untuk dapat membersihkan anak-anak menggunakan ON DELETE CASCADE
yang Anda atur. Ini karena bergantung pada kunci asing tersebut untuk mengetahui baris anak mana yang akan dihapus. Setelah SqlAlchemy mengatur semuanya ke NULL
, database tidak dapat menghapusnya. Mengatur passive_deletes=True
mencegah SqlAlchemy NULL
keluar dari kunci asing.
Anda dapat membaca lebih lanjut tentang penghapusan pasif di dokumen SqlAlchemy .
pilihan 2
Cara lain yang dapat Anda lakukan adalah membiarkan SqlAlchemy melakukannya untuk Anda. Ini diatur menggunakan cascade
argumen dari relationship
. Jika Anda memiliki hubungan yang ditentukan pada tabel induk, terlihat seperti ini:
children = relationship('Child', cascade='all,delete', backref='parent')
Jika hubungannya ada pada anak, lakukan seperti ini:
parent = relationship('Parent', backref=backref('children', cascade='all,delete'))
Sekali lagi, ini adalah anak sehingga Anda harus memanggil metode yang dipanggil backref
dan meletakkan data kaskade di sana.
Dengan ini, ketika Anda menghapus baris induk, SqlAlchemy akan benar-benar menjalankan pernyataan delete untuk Anda membersihkan baris anak. Ini kemungkinan tidak akan seefisien membiarkan database ini menangani jika untuk Anda jadi saya tidak merekomendasikannya.
Berikut adalah dokumen SqlAlchemy tentang fitur berjenjang yang didukungnya.