Post piuttosto vecchio, ma ho appena trascorso un'ora o due su questo, quindi volevo condividere la mia scoperta, soprattutto perché alcuni degli altri commenti elencati non sono del tutto corretti.
TL; DR
Dai alla tabella figlia una straniera o modifica quella esistente, aggiungendo ondelete='CASCADE'
:
parent_id = db.Column(db.Integer, db.ForeignKey('parent.id', ondelete='CASCADE'))
E una delle seguenti relazioni:
a) Questo nella tabella genitore:
children = db.relationship('Child', backref='parent', passive_deletes=True)
b) O questo sul tavolo del bambino:
parent = db.relationship('Parent', backref=backref('children', passive_deletes=True))
Dettagli
Prima di tutto, nonostante quello che dice la risposta accettata, la relazione genitore / figlio non viene stabilita usando relationship
, ma stabilita usando ForeignKey
. Puoi metterlo relationship
sulla tabella padre o figlio e funzionerà bene. Sebbene, a quanto pare, nelle tabelle figlie, devi usare la backref
funzione oltre all'argomento della parola chiave.
Opzione 1 (preferita)
In secondo luogo, SqlAlchemy supporta due diversi tipi di cascata. Il primo, e quello che consiglio, è integrato nel database e di solito assume la forma di un vincolo sulla dichiarazione di chiave esterna. In PostgreSQL assomiglia a questo:
CONSTRAINT child_parent_id_fkey FOREIGN KEY (parent_id)
REFERENCES parent_table(id) MATCH SIMPLE
ON DELETE CASCADE
Ciò significa che quando elimini un record da parent_table
, tutte le righe corrispondenti child_table
verranno eliminate per te dal database. È veloce e affidabile e probabilmente è la soluzione migliore. Lo imposti in SqlAlchemy in ForeignKey
questo modo (parte della definizione della tabella figlio):
parent_id = db.Column(db.Integer, db.ForeignKey('parent.id', ondelete='CASCADE'))
parent = db.relationship('Parent', backref=backref('children', passive_deletes=True))
Il ondelete='CASCADE'
è la parte che crea la ON DELETE CASCADE
sul tavolo.
Gotcha!
C'è un avvertimento importante qui. Notare come ho relationship
specificato con passive_deletes=True
? Se non ce l'hai, l'intera cosa non funzionerà. Questo perché per impostazione predefinita, quando elimini un record principale, SqlAlchemy fa qualcosa di veramente strano. Imposta le chiavi esterne di tutte le righe figlie su NULL
. Quindi, se elimini una riga da parent_table
dove id
= 5, fondamentalmente verrà eseguita
UPDATE child_table SET parent_id = NULL WHERE parent_id = 5
Perché vorresti questo non ne ho idea. Sarei sorpreso se molti motori di database consentissero anche di impostare una chiave esterna valida NULL
, creando un orfano. Sembra una cattiva idea, ma forse c'è un caso d'uso. Ad ogni modo, se lasci fare a SqlAlchemy, impedirai al database di essere in grado di ripulire i figli usando il ON DELETE CASCADE
che hai impostato. Questo perché si basa su quelle chiavi esterne per sapere quali righe figlie eliminare. Una volta che SqlAlchemy li ha impostati tutti su NULL
, il database non può eliminarli. L'impostazione di passive_deletes=True
impedisce a SqlAlchemy di NULL
estrarre le chiavi esterne.
Puoi leggere ulteriori informazioni sulle eliminazioni passive nei documenti di SqlAlchemy .
opzione 2
L'altro modo in cui puoi farlo è lasciare che SqlAlchemy lo faccia per te. Questo viene impostato utilizzando l' cascade
argomento di relationship
. Se hai la relazione definita nella tabella genitore, è simile a questa:
children = relationship('Child', cascade='all,delete', backref='parent')
Se la relazione è sul bambino, lo fai in questo modo:
parent = relationship('Parent', backref=backref('children', cascade='all,delete'))
Di nuovo, questo è il bambino, quindi devi chiamare un metodo chiamato backref
e inserire i dati a cascata lì.
Con questa impostazione, quando elimini una riga padre, SqlAlchemy eseguirà effettivamente le istruzioni di eliminazione per ripulire le righe figlio. Questo probabilmente non sarà efficiente come lasciare che questo database gestisca se per te, quindi non lo consiglio.
Di seguito sono riportati i documenti di SqlAlchemy sulle funzionalità a cascata che supporta.