your example shows two relationships to the identical target class: 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 Fri, Dec 18, 2020, at 8:40 AM, Mark Aquino wrote: > Thanks. That could work. The reason we need specific relationships to the > child class types is for our front end when it queries the data, which we use > sqlalchemy-graphene to do, if we query the base class relationship it returns > empty rows and makes pagination impossible as the front end is unable to know > how many rows containing the desired type are present when the mixer table > contains references to several different classes by using the id shared on > the base class. > > Mark Aquino > > *From:* sqlalchemy@googlegroups.com <sqlalchemy@googlegroups.com> on behalf > of Mike Bayer <mike...@zzzcomputing.com> > *Sent:* Thursday, December 17, 2020 9:32:40 PM > *To:* noreply-spamdigest via sqlalchemy <sqlalchemy@googlegroups.com> > *Subject:* Re: [sqlalchemy] Re: Can't delete cascade many-to-many with > polymorphic relationships unless using lazy=dynamic > > perhaps you are looking to set viewonly=True on this second relationship? > that will exclude it from any kind of persistence operation. it will only > be used to load things in from the database but not accept or honor any > mutations of data. > > not sure why you need to have two relationships that are identical, but if > you do, then yes you want only one of them to be the one that writes changes > to the DB. > > On Thu, Dec 17, 2020, at 7:40 PM, maqui...@gmail.com wrote: >> My operating assumption is that sqlalchemy looks at each relationship and >> tries to delete it, but since the previous relationship to the same base >> class was already deleted, it throws the exception and the session rolls >> back. >> >> The error from above is essentially the same as the actual error in my code >> base. I guess ideally there would be a way to just disable that >> functionality but my solution works. Just will take several hours to change >> everything correctly like this because we have about 200 models that are all >> interconnected like this. I apologize for not giving a proper working >> example I would have needed to create a brand new project unfortunately >> because the code base is so complicated >> On Thursday, December 17, 2020 at 7:32:43 PM UTC-5 maqui...@gmail.com wrote: >>> 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/01d99aca-6924-4ea0-99ff-4f759f45f985n%40googlegroups.com >> >> <https://groups.google.com/d/msgid/sqlalchemy/01d99aca-6924-4ea0-99ff-4f759f45f985n%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+unsubscr...@googlegroups.com. > To view this discussion on the web visit > https://groups.google.com/d/msgid/sqlalchemy/1ccba080-f91d-42bb-9eb6-65740efe0f0f%40www.fastmail.com > > <https://groups.google.com/d/msgid/sqlalchemy/1ccba080-f91d-42bb-9eb6-65740efe0f0f%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+unsubscr...@googlegroups.com. > To view this discussion on the web visit > https://groups.google.com/d/msgid/sqlalchemy/BL0PR16MB2515B4FCF9DDA84EC28718E0F0C30%40BL0PR16MB2515.namprd16.prod.outlook.com > > <https://groups.google.com/d/msgid/sqlalchemy/BL0PR16MB2515B4FCF9DDA84EC28718E0F0C30%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/497470b1-211d-4ae0-a488-ef80db78b96e%40www.fastmail.com.