Saltar al contenido

SQLAlchemy: eliminación en cascada

No dudes en compartir nuestra web y códigos con otro, ayúdanos a aumentar nuestra comunidad.

Solución:

El problema es que sqlalchemy considera Child como padre, porque ahí es donde definiste tu relación (no le importa que la llames “Niño”, por supuesto).

Si define la relación en el Parent clase en su lugar, funcionará:

children = relationship("Child", cascade="all,delete", backref="parent")

(Nota "Child" como un string: esto está permitido cuando se usa el estilo declarativo, por lo que puede hacer referencia a una clase que aún no está definida)

Es posible que desee agregar delete-orphan así como (delete hace que los niños se eliminen cuando se elimina el padre, delete-orphan también elimina los hijos que fueron “eliminados” del padre, incluso si el padre no se elimina)

EDITAR: acabo de descubrir: si De Verdad quiere definir la relación en el Child clase, puede hacerlo, pero tendrá que definir la cascada en la espalda (creando el backref explícitamente), así:

parent = relationship(Parent, backref=backref("children", cascade="all,delete"))

(Insinuando from sqlalchemy.orm import backref)

La respuesta de @ Steven es buena cuando borras a través de session.delete() que nunca pasa en mi caso. Noté que la mayor parte del tiempo borro a través de session.query().filter().delete() (que no coloca elementos en la memoria y los elimina directamente de la base de datos). Usando este método sqlalchemy’s cascade='all, delete' no funciona. Aunque existe una solución: ON DELETE CASCADE a través de db (nota: no todas las bases de datos lo admiten).

class Child(Base):
    __tablename__ = "children"

    id = Column(Integer, primary_key=True)
    parent_id = Column(Integer, ForeignKey("parents.id", ondelete='CASCADE'))

class Parent(Base):
    __tablename__ = "parents"

    id = Column(Integer, primary_key=True)
    child = relationship(Child, backref="parent", passive_deletes=True)

Una publicación bastante antigua, pero acabo de pasar una hora o dos en esto, así que quería compartir mi hallazgo, especialmente porque algunos de los otros comentarios enumerados no son del todo correctos.

TL; DR

Dele a la tabla secundaria una ajena o modifique la existente, agregando ondelete='CASCADE':

parent_id = db.Column(db.Integer, db.ForeignKey('parent.id', ondelete='CASCADE'))

Y uno de las siguientes relaciones:

a) Esto en la mesa principal:

children = db.relationship('Child', backref='parent', passive_deletes=True)

B) O esto en la mesa del niño:

parent = db.relationship('Parent', backref=backref('children', passive_deletes=True))

Detalles

En primer lugar, a pesar de lo que dice la respuesta aceptada, la relación padre / hijo no se establece mediante el uso de relationship, se establece mediante el uso ForeignKey. Puedes poner el relationship en la tabla principal o secundaria y funcionará bien. Aunque, aparentemente en las tablas secundarias, debe usar el backref función además del argumento de palabra clave.

Opción 1 (preferida)

En segundo lugar, SqlAlchemy admite dos tipos diferentes de conexión en cascada. El primero, y el que recomiendo, está integrado en su base de datos y generalmente toma la forma de una restricción en el extranjero. key declaración. En PostgreSQL se ve así:

CONSTRAINT child_parent_id_fkey FOREIGN KEY (parent_id)
REFERENCES parent_table(id) MATCH SIMPLE
ON DELETE CASCADE

Esto significa que cuando elimina un registro de parent_table, luego todas las filas correspondientes en child_table será eliminado por la base de datos. Es rápido y confiable y probablemente sea su mejor opción. Configuraste esto en SqlAlchemy a través de ForeignKey así (parte de la definición de la tabla secundaria):

parent_id = db.Column(db.Integer, db.ForeignKey('parent.id', ondelete='CASCADE'))
parent = db.relationship('Parent', backref=backref('children', passive_deletes=True))

los ondelete='CASCADE' es la parte que crea el ON DELETE CASCADE en la mesa.

¡Entendido!

Aquí hay una advertencia importante. Note como tengo un relationship especificado con passive_deletes=True? Si no tiene eso, todo no funcionará. Esto se debe a que, de forma predeterminada, cuando elimina un registro principal, SqlAlchemy hace algo realmente extraño. Establece lo extranjero keys de todas las filas secundarias a NULL. Entonces, si elimina una fila de parent_table dónde id = 5, entonces básicamente se ejecutará

UPDATE child_table SET parent_id = NULL WHERE parent_id = 5

Por qué querrías esto, no tengo ni idea. Me sorprendería si muchos motores de base de datos le permitieran establecer un valor externo válido key para NULL, creando un huérfano. Parece una mala idea, pero tal vez haya un caso de uso. De todos modos, si deja que SqlAlchemy haga esto, evitará que la base de datos pueda limpiar a los niños usando el ON DELETE CASCADE que configuró. Esto se debe a que depende de los extranjeros keys para saber qué filas secundarias eliminar. Una vez que SqlAlchemy los haya configurado todos en NULL, la base de datos no puede eliminarlos. Establecer el passive_deletes=True evita que SqlAlchemy NULLsalir del extranjero keys.

Puede leer más sobre eliminaciones pasivas en los documentos de SqlAlchemy.

opcion 2

La otra forma en que puede hacerlo es dejar que SqlAlchemy lo haga por usted. Esto se configura usando el cascade argumento de la relationship. Si tiene la relación definida en la tabla principal, se ve así:

children = relationship('Child', cascade='all,delete', backref='parent')

Si la relación es del niño, hazlo así:

parent = relationship('Parent', backref=backref('children', cascade='all,delete'))

Nuevamente, este es el niño, por lo que debe llamar a un método llamado backref y poner los datos en cascada allí.

Con esto en su lugar, cuando elimina una fila principal, SqlAlchemy ejecutará declaraciones de eliminación para que pueda limpiar las filas secundarias. Es probable que esto no sea tan eficiente como dejar que esta base de datos se encargue de usted, así que no lo recomiendo.

Aquí están los documentos de SqlAlchemy sobre las funciones en cascada que admite.

Reseñas y puntuaciones

Puedes añadir valor a nuestro contenido tributando tu experiencia en las interpretaciones.

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *