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 cascadein 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:
