Postagem bem antiga, mas acabei de gastar uma ou duas horas nisso, então gostaria de compartilhar minha descoberta, especialmente porque alguns dos outros comentários listados não estão certos.
TL; DR
Dê à tabela filho um estrangeiro ou modifique o existente, adicionando ondelete='CASCADE'
:
parent_id = db.Column(db.Integer, db.ForeignKey('parent.id', ondelete='CASCADE'))
E um dos seguintes relacionamentos:
a) Isso na tabela pai:
children = db.relationship('Child', backref='parent', passive_deletes=True)
b) Ou isso na mesa infantil:
parent = db.relationship('Parent', backref=backref('children', passive_deletes=True))
Detalhes
Em primeiro lugar, apesar do que diz a resposta aceita, a relação pai / filho não se estabelece usando relationship
, é estabelecida usando ForeignKey
. Você pode colocar o relationship
nas tabelas pai ou filho e funcionará bem. Embora, aparentemente nas tabelas filho, você tenha que usar a backref
função além do argumento de palavra-chave.
Opção 1 (preferencial)
Em segundo lugar, SqlAlchemy oferece suporte a dois tipos diferentes de cascata. O primeiro, e o que eu recomendo, está embutido em seu banco de dados e geralmente assume a forma de uma restrição na declaração de chave estrangeira. No PostgreSQL, é assim:
CONSTRAINT child_parent_id_fkey FOREIGN KEY (parent_id)
REFERENCES parent_table(id) MATCH SIMPLE
ON DELETE CASCADE
Isso significa que quando você exclui um registro de parent_table
, todas as linhas correspondentes em child_table
serão excluídas para você pelo banco de dados. É rápido e confiável e provavelmente sua melhor aposta. Você configura isso no SqlAlchemy ForeignKey
assim (parte da definição da tabela filho):
parent_id = db.Column(db.Integer, db.ForeignKey('parent.id', ondelete='CASCADE'))
parent = db.relationship('Parent', backref=backref('children', passive_deletes=True))
A ondelete='CASCADE'
é a parte que cria o ON DELETE CASCADE
sobre a mesa.
Peguei vocês!
Há uma advertência importante aqui. Observe como eu tenho um relationship
especificado com passive_deletes=True
? Se você não tiver isso, a coisa toda não funcionará. Isso ocorre porque, por padrão, quando você exclui um registro pai, o SqlAlchemy faz algo realmente estranho. Ele define as chaves estrangeiras de todas as linhas filhas como NULL
. Portanto, se você excluir uma linha de parent_table
onde id
= 5, ela basicamente executará
UPDATE child_table SET parent_id = NULL WHERE parent_id = 5
Por que você iria querer isso, eu não tenho ideia. Eu ficaria surpreso se muitos mecanismos de banco de dados permitissem que você definisse uma chave estrangeira válida para NULL
, criando um órfão. Parece uma má ideia, mas talvez haja um caso de uso. De qualquer forma, se você deixar o SqlAlchemy fazer isso, você impedirá que o banco de dados seja capaz de limpar os filhos usando o ON DELETE CASCADE
que você configurou. Isso ocorre porque ele depende dessas chaves estrangeiras para saber quais linhas filho excluir. Depois que o SqlAlchemy definir todos como NULL
, o banco de dados não poderá excluí-los. A configuração passive_deletes=True
evita que o SqlAlchemy NULL
saia das chaves estrangeiras.
Você pode ler mais sobre exclusões passivas nos documentos do SqlAlchemy .
opção 2
A outra maneira de fazer isso é deixar o SqlAlchemy fazer isso por você. Isso é configurado usando o cascade
argumento do relationship
. Se você tiver o relacionamento definido na tabela pai, será assim:
children = relationship('Child', cascade='all,delete', backref='parent')
Se o relacionamento é por conta da criança, você faz assim:
parent = relationship('Parent', backref=backref('children', cascade='all,delete'))
Novamente, este é o filho, então você deve chamar um método chamado backref
e colocar os dados em cascata nele.
Com isso no lugar, quando você exclui uma linha pai, SqlAlchemy irá realmente executar instruções delete para você limpar as linhas filho. Provavelmente, isso não será tão eficiente quanto deixar esse banco de dados manipular se for para você, então eu não o recomendo.
Aqui estão os documentos do SqlAlchemy sobre os recursos em cascata suportados.