I am getting the following error when I try to delete all entries from parent
table
sqlalchemy.exc.IntegrityError: (psycopg2.errors.ForeignKeyViolation) update or delete on table "parent" violates foreign key constraint "child_p_id_fkey" on table "child" DETAIL: Key (id)=(1) is still referenced from table "child".
This is my code, and I have already tried adding cascade
in either ends of relationship
as per SQLAlchemy: cascade delete, in which case I get this instead
sqlalchemy.exc.ArgumentError: For many-to-one relationship Child.parent, delete-orphan cascade is normally configured only on the "one" side of a one-to-many relationship, and not on the "many" side of a many-to-one or many-to-many relationship. To force this relationship to allow a particular "Parent" object to be referred towards by only a single "Child" object at a time via the Child.parent relationship, which would allow delete-orphan cascade to take place in this direction, set the single_parent=True flag. (Background on this error at: https://sqlalche.me/e/14/bbf0)
from sqlalchemy import create_engine, Column, ForeignKey, Integer, delete
from sqlalchemy.orm import declarative_base, relationship, Session
Base = declarative_base()
class Parent(Base):
__tablename__ = "parent"
id = Column(Integer, primary_key=True)
children = relationship(
"Child",
back_populates="parent",
cascade="save-update, merge, delete, delete-orphan",
)
class Child(Base):
__tablename__ = "child"
id = Column(Integer, primary_key=True)
p_id = Column(Integer, ForeignKey(Parent.id))
parent = relationship(Parent, back_populates="children")
engine = create_engine(
"postgresql://user:pass@localhost:5432/postgres", future=True
)
Base.metadata.create_all(engine)
with Session(engine) as session, session.begin():
foo = Parent()
session.add(foo)
session.add(Child(parent=foo))
with Session(engine) as session:
session.execute(delete(Parent).where(Parent.id == 1))
CodePudding user response:
As I understand it when you use something like delete
directly you step outside the orm. For the relationship cascades to execute you need to use session.delete(foo)
.
ORM DELETE
So in your last block you'd need to do something like this to trigger the cascades in your relationship
on Parent
:
with Session(engine) as session:
# Select the parent back from the db
foo = session.get(Parent, 1)
# Now delete it with the ORM
session.delete(foo)
BULK DELETE / CORE DELETE
To cascade when using session.execute(delete(Parent).where(Parent.id == 1))
you need to set ondelete='CASCADE'
on the foreign key like this:
class Child(Base):
__tablename__ = "child"
id = Column(Integer, primary_key=True)
p_id = Column(Integer, ForeignKey(Parent.id, ondelete='CASCADE'))
parent = relationship(Parent, back_populates="children")
Combining relationship(..., cascade="...")
and ondelete=...
together is explained here: