1. target database = postgres, example queries and stacktrace:
>>> from webapp.database.orm.models import ParentClass, ChildClass, ChildChildClass >>> p = ParentClass() >>> c = ChildClass() >>> cc = ChildChildClass() >>> c.children.append(cc) >>> p.children.append(c) >>> session.add(p) >>> session.commit() >>> p = session.query(ParentClass).one() >>> [cc for c in p.children for cc in c.children] [ChildChildClass(id=UUID('1253a435-3330-4e36-bafc-ad8ff5176c4d'))] >>> session.delete(p) >>> session.flush() Traceback (most recent call last): File "<console>", line 1, in <module> File "/Users/aquinmx3/venv/espresso3.8/lib/python3.8/site-packages/sqlalchemy/orm/session.py", line 2496, in flush self._flush(objects) File "/Users/aquinmx3/venv/espresso3.8/lib/python3.8/site-packages/sqlalchemy/orm/session.py", line 2637, in _flush transaction.rollback(_capture_exception=True) File "/Users/aquinmx3/venv/espresso3.8/lib/python3.8/site-packages/sqlalchemy/util/langhelpers.py", line 68, in __exit__ compat.raise_( File "/Users/aquinmx3/venv/espresso3.8/lib/python3.8/site-packages/sqlalchemy/util/compat.py", line 178, in raise_ raise exception File "/Users/aquinmx3/venv/espresso3.8/lib/python3.8/site-packages/sqlalchemy/orm/session.py", line 2597, in _flush flush_context.execute() File "/Users/aquinmx3/venv/espresso3.8/lib/python3.8/site-packages/sqlalchemy/orm/unitofwork.py", line 422, in execute rec.execute(self) File "/Users/aquinmx3/venv/espresso3.8/lib/python3.8/site-packages/sqlalchemy/orm/unitofwork.py", line 538, in execute self.dependency_processor.process_deletes(uow, states) File "/Users/aquinmx3/venv/espresso3.8/lib/python3.8/site-packages/sqlalchemy/orm/dependency.py", line 1104, in process_deletes self._run_crud( File "/Users/aquinmx3/venv/espresso3.8/lib/python3.8/site-packages/sqlalchemy/orm/dependency.py", line 1201, in _run_crud raise exc.StaleDataError( sqlalchemy.orm.exc.StaleDataError: DELETE statement on table 'mix_child_class_child_child_class' expected to delete 1 row(s); Only 0 were matched. >>> KeyboardInterrupt Works when ChildClass is declared as follows instead of as written in original question: class ChildClass(Base): __tablename__ = "child_class" id = Column("id", UUID(as_uuid=True), primary_key=True, default=uuid4()) parents = relationship( "ParentClass", secondary="mix_parent_class_child_class", passive_deletes=True, ) children = relationship( "ChildChildClass", secondary="mix_child_class_child_child_class", cascade="all, delete", ) children2 = relationship( "ChildChildClass", secondary="mix_child_class_child_child_class", passive_deletes=True ) >>> from webapp.database.orm.models import ParentClass, ChildClass, ChildChildClass >>> p = ParentClass() >>> c = ChildClass() >>> cc = ChildChildClass() >>> c.children.append(cc) >>> p.children.append(c) >>> session.add(p) >>> session.commit() >>> p = session.query(ParentClass).one() >>> session.delete(p) >>> session.commit() On Thursday, December 17, 2020 at 7:25:58 PM UTC-5 Mike Bayer wrote: > > > On Thu, Dec 17, 2020, at 7:15 PM, Mark Aquino wrote: > > They seem to be runnable except the base should be= DeclarativeMeta > > > OK, runnable example is below. > > how about: > > 1. target database type (Postgresql?) > > 2. sample data, inserts, etc. > > 3. sample queries > > 4. operation that fails > > 5. stack trace, etc > > > thanks! > > from uuid import uuid4 > > from sqlalchemy import Column > from sqlalchemy.dialects.postgresql.base import UUID > from sqlalchemy.orm import relationship > from sqlalchemy.ext.declarative import declarative_base > > Base = declarative_base() > > > class ChildClass(Base): > __tablename__ = "child_class" > id = Column("id", UUID(as_uuid=True), primary_key=True, > default=uuid4()) > parents = relationship( > "ParentClass", > secondary="mix_parent_class_child_class", > passive_deletes=True, > ) > children = relationship( > "ChildChildClass", > secondary="mix_child_class_child_child_class", > cascade="all, delete", > ) > children2 = relationship( > "ChildChildClass", > secondary="mix_child_class_child_child_class", > cascade="all, delete", > ) > > > class ChildChildClass(Base): > __tablename__ = "child_child_class" > id = Column("id", UUID(as_uuid=True), primary_key=True, > default=uuid4()) > parents = relationship( > "ChildClass", > secondary="mix_child_class_child_child_class", > passive_deletes=True, > ) > > > class ParentClass(Base): > __tablename__ = "parent_class" > id = Column("id", UUID(as_uuid=True), primary_key=True, > default=uuid4()) > children = relationship( > "ChildClass", > secondary="mix_parent_class_child_class", > cascade="all, delete", > ) > > > from sqlalchemy.sql.schema import Table, ForeignKey, Column, > UniqueConstraint > > > mix_parent_class_child_class = Table( > "mix_parent_class_child_class", > Base.metadata, > Column( > "parent_class_id", > ForeignKey("parent_class.id", ondelete="CASCADE"), > nullable=False, > ), > Column("child_class_id", ForeignKey("child_class.id"), > nullable=False), > UniqueConstraint( > "parent_class_id", > "child_class_id", > name="uix_parent_class_child_class", > ), > ) > > mix_child_class_child_child_class = Table( > "mix_child_class_child_child_class", > Base.metadata, > Column( > "child_class_id", > ForeignKey("child_class.id", ondelete="CASCADE"), > nullable=False, > ), > Column( > "child_child_class_id", > ForeignKey("child_child_class.id"), > nullable=False, > ), > UniqueConstraint( > "child_class_id", > "child_child_class_id", > name="uix_child_class_child_child_class", > ), > ) > > > > > > The relationships aren’t to the same target in real life, > > It’s like this: > > Class BaseClass: > ... > > Class SubClassA(BaseClass) > ... > > Class SubclassB(BaseClass): > ... > > (Plus Many other subclasses) > > A Mixer “BaseClassBaseClass” such that child subclassA and subclassB are > both present in mix_base_class_bass_class > > BaseClass has relationships to BaseClass, SubClassA and SubClassB, so you > can access only SubClassAs and SubClassBs or all SubClassXs that could be > filtered to whatever subclass you need. All these relationships use the > same mixer table, because they all share a primary key with BaseClass (and > removing the need to make explicit mixed tables for every subclass). > > Mark Aquino > ------------------------------ > > *From:* sqlal...@googlegroups.com <sqlal...@googlegroups.com> on behalf > of Mike Bayer <mik...@zzzcomputing.com> > *Sent:* Thursday, December 17, 2020 7:08:58 PM > *To:* noreply-spamdigest via sqlalchemy <sqlal...@googlegroups.com> > *Subject:* Re: [sqlalchemy] Re: Can't delete cascade many-to-many with > polymorphic relationships unless using lazy=dynamic > > your examples aren't complete or runnable so I don't really know what the > issue is, although having two relationships to the same target class seems > a little unusual and I'm not sure why you'd need that. > > > On Thu, Dec 17, 2020, at 6:01 PM, maqui...@gmail.com wrote: > > > I think I may have just found a solution? Not sure if this is correct but > it looks like it worked when i changed the "extra" relationship to > passive_deletes=True instead of cascade > > class ChildClass(XPressoBase): > __tablename__ = "child_class" > id = Column("id", UUID(as_uuid=True), primary_key=True, default=uuid4()) > parents = relationship("ParentClass", > secondary="mix_parent_class_child_class", passive_deletes=True) > children = relationship("ChildChildClass", > secondary="mix_child_class_child_child_class", cascade="all, delete") > children2 = relationship("ChildChildClass", > secondary="mix_child_class_child_child_class", passive_deletes=True) > On Thursday, December 17, 2020 at 5:50:06 PM UTC-5 maqui...@gmail.com > wrote: > > I have a polymorphic data model where association tables are to base > classes and some relationships that link to different child classes to > filter out the non-matching base classes, however these seem to result in > sqlalchemy being unable to delete cascade properly > > In a real case lets say i have > class Base(): > children = relationship("Base", secondary="mix_base_base", > cascade="all, delete") > someTypes = relationship("SomeType", secondary="mix_base_base", > cascade="all, delete") > other_types = relationship("OtherType", secondary="mix_base_base", > cascade="all") > > class SomeType(Base): > parents = relationship("Base", secondary="mix_base_base", > passive_deletes=True) > > class OtherType(Base): > parents = relationship("Base", secondary="mix_base_base", > passive_deletes=True) > > > if I delete a base that doesn't have relationships to SomeType and > OtherType, then everything works great. However once I add those extra > relationships sqlalchemy no longer deletes the children relationships. > This can be overcome by using lazy="dynamic" on the relationships, but at > GREAT performance cost (about 100%), which isn't good. > > Does anyone know how to get around this without using dynamic loading? The > specific relationships are important for front end pagination of data, but > the performance cost is too great for the amount of data in this system to > double the query times. > > a simplified and stupid workable example where I just add a redundant > children2 relationship to ChildClass, resulting in the same problem: > > from uuid import uuid4 > > from sqlalchemy import Column > from sqlalchemy.dialects.postgresql.base import UUID > from sqlalchemy.orm import relationship > > from webapp.database.orm.base import XPressoBase > > class ChildClass(Base): > __tablename__ = "child_class" > id = Column("id", UUID(as_uuid=True), primary_key=True, default=uuid4()) > parents = relationship("ParentClass", > secondary="mix_parent_class_child_class", passive_deletes=True) > children = relationship("ChildChildClass", > secondary="mix_child_class_child_child_class", cascade="all, delete") > children2 = relationship("ChildChildClass", > secondary="mix_child_class_child_child_class", cascade="all, delete") > > class ChildChildClass(Base): > __tablename__ = "child_child_class" > id = Column("id", UUID(as_uuid=True), primary_key=True, default=uuid4()) > parents = relationship("ChildClass", > secondary="mix_child_class_child_child_class", passive_deletes=True) > > class ParentClass(Base): > __tablename__ = "parent_class" > id = Column("id", UUID(as_uuid=True), primary_key=True, default=uuid4()) > children = relationship("ChildClass", > secondary="mix_parent_class_child_class", cascade="all, delete") > from sqlalchemy.sql.schema import Table, ForeignKey, Column, > UniqueConstraint > > from webapp.database.orm.base import XPressoBase > > mix_parent_class_child_class = Table( > "mix_parent_class_child_class", > XPressoBase.metadata, > Column("parent_class_id", ForeignKey("parent_class.id", > ondelete="CASCADE"), nullable=False), > Column("child_class_id", ForeignKey("child_class.id"), nullable=False), > UniqueConstraint( > "parent_class_id", "child_class_id", name="uix_parent_class_child_class" > ), > ) > > mix_child_class_child_child_class = Table( > "mix_child_class_child_child_class", > XPressoBase.metadata, > Column("child_class_id", ForeignKey("child_class.id", > ondelete="CASCADE"), nullable=False), > Column("child_child_class_id", ForeignKey("child_child_class.id"), > nullable=False), > UniqueConstraint( > "child_class_id", "child_child_class_id", > name="uix_child_class_child_child_class" > ), > ) > > > -- > SQLAlchemy - > The Python SQL Toolkit and Object Relational Mapper > > http://www.sqlalchemy.org/ > > To post example code, please provide an MCVE: Minimal, Complete, and > Verifiable Example. See http://stackoverflow.com/help/mcve for a full > description. > --- > You received this message because you are subscribed to the Google Groups > "sqlalchemy" group. > To unsubscribe from this group and stop receiving emails from it, send an > email to sqlalchemy+...@googlegroups.com. > To view this discussion on the web visit > https://groups.google.com/d/msgid/sqlalchemy/a172d1cf-7105-48d8-a1ea-50889fa6e5c7n%40googlegroups.com > > <https://groups.google.com/d/msgid/sqlalchemy/a172d1cf-7105-48d8-a1ea-50889fa6e5c7n%40googlegroups.com?utm_medium=email&utm_source=footer> > . > > > > -- > SQLAlchemy - > The Python SQL Toolkit and Object Relational Mapper > > http://www.sqlalchemy.org/ > > To post example code, please provide an MCVE: Minimal, Complete, and > Verifiable Example. See http://stackoverflow.com/help/mcve for a full > description. > --- > You received this message because you are subscribed to the Google Groups > "sqlalchemy" group. > To unsubscribe from this group and stop receiving emails from it, send an > email to sqlalchemy+...@googlegroups.com. > To view this discussion on the web visit > https://groups.google.com/d/msgid/sqlalchemy/ab17a1d6-e936-4a7c-bbc9-e0459d1e6da0%40www.fastmail.com > > <https://groups.google.com/d/msgid/sqlalchemy/ab17a1d6-e936-4a7c-bbc9-e0459d1e6da0%40www.fastmail.com?utm_medium=email&utm_source=footer> > . > > > -- > SQLAlchemy - > The Python SQL Toolkit and Object Relational Mapper > > http://www.sqlalchemy.org/ > > To post example code, please provide an MCVE: Minimal, Complete, and > Verifiable Example. See http://stackoverflow.com/help/mcve for a full > description. > --- > You received this message because you are subscribed to the Google Groups > "sqlalchemy" group. > To unsubscribe from this group and stop receiving emails from it, send an > email to sqlalchemy+...@googlegroups.com. > > To view this discussion on the web visit > https://groups.google.com/d/msgid/sqlalchemy/BL0PR16MB251536B80589AC7738798A8DF0C30%40BL0PR16MB2515.namprd16.prod.outlook.com > > <https://groups.google.com/d/msgid/sqlalchemy/BL0PR16MB251536B80589AC7738798A8DF0C30%40BL0PR16MB2515.namprd16.prod.outlook.com?utm_medium=email&utm_source=footer> > . > > > -- SQLAlchemy - The Python SQL Toolkit and Object Relational Mapper http://www.sqlalchemy.org/ To post example code, please provide an MCVE: Minimal, Complete, and Verifiable Example. See http://stackoverflow.com/help/mcve for a full description. --- You received this message because you are subscribed to the Google Groups "sqlalchemy" group. To unsubscribe from this group and stop receiving emails from it, send an email to sqlalchemy+unsubscr...@googlegroups.com. To view this discussion on the web visit https://groups.google.com/d/msgid/sqlalchemy/d8efd08d-dbaa-4d1d-8dec-6d8091713821n%40googlegroups.com.