Wow, this has been a problem for me for the past 3 or 4 days and took a while to get to that example. Defining the backrefs or the relationships in the opposite direction did the job. I had intentionally left out some of the backreffed relationships because I didn't need them for what I was working on, but it definitely is worth it to get this working.
Thanks a lot. -Will On Wed, Dec 15, 2010 at 2:45 PM, Michael Bayer <mike...@zzzcomputing.com> wrote: > This is an interesting edge case and I can probably ensure that the > dependency between Parent/Child is present in the unit of work even if there > is no known linkage at the Child.parent level for the objects actually > present - ticket #2002 is added for this. > > In the meantime, the uow needs to be aware of the linkage between > Parent->Child when flush occurs. Adding a backref "children" to the parent > relationship will do it, or ensuring that child.parent is accessed before > emitting the flush will do it. The usual way this kind of delete is > performed is the "delete" cascade is added to the "children" backref, then > the Parent is deleted alone, the deletes cascading to the Child objects > naturally. > > But this is a fun bug and I'll probably have a fix very soon, perhaps in 10 > minutes or maybe not. > > > > > On Dec 15, 2010, at 2:17 PM, Will wrote: > >> """ >> Hello, >> >> I've been recently having a problem with sqlalchemy not flushing >> deletes in the proper order. I've created a simple example for the >> problem that has been occuring. I tried to run this using sqlite and >> it doesn't have any problems, it is only with Postgresql. >> >> One thing of note is that if there is only one Child it doesn't seem >> to >> have a problem, only when there are multiple children. Not sure if >> that makes a difference in the SQLAlchemy code. >> """ >> >> from sqlalchemy import create_engine >> from sqlalchemy.ext.declarative import declarative_base >> from sqlalchemy.orm import scoped_session, sessionmaker, relationship >> from sqlalchemy.schema import Column, ForeignKey >> from sqlalchemy.types import Integer >> >> #engine = create_engine('sqlite:///') >> engine = create_engine('postgresql://test_runner@/testing_db') >> >> Model = declarative_base() >> >> >> class Parent(Model): >> __tablename__ = 'parents' >> >> id = Column(Integer, primary_key=True) >> >> >> class Child(Model): >> __tablename__ = 'children' >> >> id = Column(Integer, primary_key=True) >> parent_id = Column(Integer, ForeignKey('parents.id'), >> nullable=False) >> parent = relationship('Parent') >> >> >> def begin(): >> """Begin transaction""" >> #global transaction >> #transaction = session.begin() >> >> session.begin() >> >> >> def commit(): >> """Commit transaction""" >> #global transaction >> #transaction.commit() >> >> session.commit() >> >> >> Model.metadata.create_all(engine) >> >> parent = Parent() >> children = [Child(parent=parent), Child(parent=parent)] >> >> Session = sessionmaker(bind=engine, autocommit=True) >> session = Session() >> >> try: >> session.bind.echo = True >> >> begin() >> session.add_all(children) >> session.add(parent) >> commit() >> >> >> begin() >> for child in children: >> session.delete(child) >> session.delete(parent) >> commit() >> >> session.bind.echo = False >> finally: >> Model.metadata.drop_all(engine) >> >> >> """ >> From running the script I have two different outputs because it seems >> to run the deletes in a random order so subsequent runs will behave >> differently. >> """ >> >> # Example Failed Run >> """ >> 2010-12-15 13:45:05,050 INFO sqlalchemy.engine.base.Engine.0x...f5d0 >> BEGIN (implicit) >> 2010-12-15 13:45:05,051 INFO sqlalchemy.engine.base.Engine.0x...f5d0 >> INSERT INTO parents DEFAULT VALUES RETURNING parents.id >> 2010-12-15 13:45:05,051 INFO sqlalchemy.engine.base.Engine.0x...f5d0 >> {} >> 2010-12-15 13:45:05,052 INFO sqlalchemy.engine.base.Engine.0x...f5d0 >> INSERT INTO children (parent_id) VALUES (%(parent_id)s) RETURNING >> children.id >> 2010-12-15 13:45:05,052 INFO sqlalchemy.engine.base.Engine.0x...f5d0 >> {'parent_id': 1} >> 2010-12-15 13:45:05,053 INFO sqlalchemy.engine.base.Engine.0x...f5d0 >> INSERT INTO children (parent_id) VALUES (%(parent_id)s) RETURNING >> children.id >> 2010-12-15 13:45:05,054 INFO sqlalchemy.engine.base.Engine.0x...f5d0 >> {'parent_id': 1} >> 2010-12-15 13:45:05,054 INFO sqlalchemy.engine.base.Engine.0x...f5d0 >> COMMIT >> 2010-12-15 13:45:05,055 INFO sqlalchemy.engine.base.Engine.0x...f5d0 >> BEGIN (implicit) >> 2010-12-15 13:45:05,056 INFO sqlalchemy.engine.base.Engine.0x...f5d0 >> SELECT parents.id AS parents_id >> FROM parents >> WHERE parents.id = %(param_1)s >> 2010-12-15 13:45:05,056 INFO sqlalchemy.engine.base.Engine.0x...f5d0 >> {'param_1': 1} >> 2010-12-15 13:45:05,057 INFO sqlalchemy.engine.base.Engine.0x...f5d0 >> DELETE FROM parents WHERE parents.id = %(id)s >> 2010-12-15 13:45:05,057 INFO sqlalchemy.engine.base.Engine.0x...f5d0 >> {'id': 1} >> 2010-12-15 13:45:05,058 INFO sqlalchemy.engine.base.Engine.0x...f5d0 >> ROLLBACK >> 2010-12-15 13:45:05,061 INFO sqlalchemy.engine.base.Engine.0x...f5d0 >> select relname from pg_class c join pg_namespace n on >> n.oid=c.relnamespace where n.nspname=current_schema() and >> lower(relname)=%(name)s >> 2010-12-15 13:45:05,061 INFO sqlalchemy.engine.base.Engine.0x...f5d0 >> {'name': u'children'} >> 2010-12-15 13:45:05,063 INFO sqlalchemy.engine.base.Engine.0x...f5d0 >> select relname from pg_class c join pg_namespace n on >> n.oid=c.relnamespace where n.nspname=current_schema() and >> lower(relname)=%(name)s >> 2010-12-15 13:45:05,063 INFO sqlalchemy.engine.base.Engine.0x...f5d0 >> {'name': u'parents'} >> 2010-12-15 13:45:05,064 INFO sqlalchemy.engine.base.Engine.0x...f5d0 >> DROP TABLE children >> 2010-12-15 13:45:05,064 INFO sqlalchemy.engine.base.Engine.0x...f5d0 >> {} >> 2010-12-15 13:45:05,066 INFO sqlalchemy.engine.base.Engine.0x...f5d0 >> COMMIT >> 2010-12-15 13:45:05,067 INFO sqlalchemy.engine.base.Engine.0x...f5d0 >> DROP TABLE parents >> 2010-12-15 13:45:05,067 INFO sqlalchemy.engine.base.Engine.0x...f5d0 >> {} >> 2010-12-15 13:45:05,068 INFO sqlalchemy.engine.base.Engine.0x...f5d0 >> COMMIT >> Traceback (most recent call last): >> File "sharded_session_issue.py", line 64, in <module> >> commit(session) >> File "sharded_session_issue.py", line 47, in commit >> session.commit() >> File "/usr/lib/pymodules/python2.6/sqlalchemy/orm/session.py", line >> 623, in commit >> self.transaction.commit() >> File "/usr/lib/pymodules/python2.6/sqlalchemy/orm/session.py", line >> 385, in commit >> self._prepare_impl() >> File "/usr/lib/pymodules/python2.6/sqlalchemy/orm/session.py", line >> 369, in _prepare_impl >> self.session.flush() >> File "/usr/lib/pymodules/python2.6/sqlalchemy/orm/session.py", line >> 1397, in flush >> self._flush(objects) >> File "/usr/lib/pymodules/python2.6/sqlalchemy/orm/session.py", line >> 1478, in _flush >> flush_context.execute() >> File "/usr/lib/pymodules/python2.6/sqlalchemy/orm/unitofwork.py", >> line 304, in execute >> rec.execute(self) >> File "/usr/lib/pymodules/python2.6/sqlalchemy/orm/unitofwork.py", >> line 471, in execute >> uow >> File "/usr/lib/pymodules/python2.6/sqlalchemy/orm/mapper.py", line >> 2076, in _delete_obj >> c = connection.execute(statement, del_objects) >> File "/usr/lib/pymodules/python2.6/sqlalchemy/engine/base.py", line >> 1191, in execute >> params) >> File "/usr/lib/pymodules/python2.6/sqlalchemy/engine/base.py", line >> 1271, in _execute_clauseelement >> return self.__execute_context(context) >> File "/usr/lib/pymodules/python2.6/sqlalchemy/engine/base.py", line >> 1302, in __execute_context >> context.parameters[0], context=context) >> File "/usr/lib/pymodules/python2.6/sqlalchemy/engine/base.py", line >> 1401, in _cursor_execute >> context) >> File "/usr/lib/pymodules/python2.6/sqlalchemy/engine/base.py", line >> 1394, in _cursor_execute >> context) >> File "/usr/lib/pymodules/python2.6/sqlalchemy/engine/default.py", >> line 299, in do_execute >> cursor.execute(statement, parameters) >> sqlalchemy.exc.IntegrityError: (IntegrityError) update or delete on >> table "parents" violates foreign key constraint >> "children_parent_id_fkey" on table "children" >> DETAIL: Key (id)=(1) is still referenced from table "children". >> 'DELETE FROM parents WHERE parents.id = %(id)s' {'id': 1} >> """ >> >> # Example Successful Run >> """ >> 2010-12-15 13:45:40,471 INFO sqlalchemy.engine.base.Engine.0x...95d0 >> BEGIN (implicit) >> 2010-12-15 13:45:40,472 INFO sqlalchemy.engine.base.Engine.0x...95d0 >> INSERT INTO parents DEFAULT VALUES RETURNING parents.id >> 2010-12-15 13:45:40,472 INFO sqlalchemy.engine.base.Engine.0x...95d0 >> {} >> 2010-12-15 13:45:40,473 INFO sqlalchemy.engine.base.Engine.0x...95d0 >> INSERT INTO children (parent_id) VALUES (%(parent_id)s) RETURNING >> children.id >> 2010-12-15 13:45:40,473 INFO sqlalchemy.engine.base.Engine.0x...95d0 >> {'parent_id': 1} >> 2010-12-15 13:45:40,474 INFO sqlalchemy.engine.base.Engine.0x...95d0 >> INSERT INTO children (parent_id) VALUES (%(parent_id)s) RETURNING >> children.id >> 2010-12-15 13:45:40,474 INFO sqlalchemy.engine.base.Engine.0x...95d0 >> {'parent_id': 1} >> 2010-12-15 13:45:40,475 INFO sqlalchemy.engine.base.Engine.0x...95d0 >> COMMIT >> 2010-12-15 13:45:40,476 INFO sqlalchemy.engine.base.Engine.0x...95d0 >> BEGIN (implicit) >> 2010-12-15 13:45:40,477 INFO sqlalchemy.engine.base.Engine.0x...95d0 >> SELECT children.id AS children_id, children.parent_id AS >> children_parent_id >> FROM children >> WHERE children.id = %(param_1)s >> 2010-12-15 13:45:40,477 INFO sqlalchemy.engine.base.Engine.0x...95d0 >> {'param_1': 1} >> 2010-12-15 13:45:40,478 INFO sqlalchemy.engine.base.Engine.0x...95d0 >> SELECT children.id AS children_id, children.parent_id AS >> children_parent_id >> FROM children >> WHERE children.id = %(param_1)s >> 2010-12-15 13:45:40,478 INFO sqlalchemy.engine.base.Engine.0x...95d0 >> {'param_1': 2} >> 2010-12-15 13:45:40,479 INFO sqlalchemy.engine.base.Engine.0x...95d0 >> DELETE FROM children WHERE children.id = %(id)s >> 2010-12-15 13:45:40,479 INFO sqlalchemy.engine.base.Engine.0x...95d0 >> ({'id': 1}, {'id': 2}) >> 2010-12-15 13:45:40,480 INFO sqlalchemy.engine.base.Engine.0x...95d0 >> SELECT parents.id AS parents_id >> FROM parents >> WHERE parents.id = %(param_1)s >> 2010-12-15 13:45:40,480 INFO sqlalchemy.engine.base.Engine.0x...95d0 >> {'param_1': 1} >> 2010-12-15 13:45:40,481 INFO sqlalchemy.engine.base.Engine.0x...95d0 >> DELETE FROM parents WHERE parents.id = %(id)s >> 2010-12-15 13:45:40,481 INFO sqlalchemy.engine.base.Engine.0x...95d0 >> {'id': 1} >> 2010-12-15 13:45:40,482 INFO sqlalchemy.engine.base.Engine.0x...95d0 >> COMMIT >> """ >> >> -- >> You received this message because you are subscribed to the Google Groups >> "sqlalchemy" group. >> To post to this group, send email to sqlalch...@googlegroups.com. >> To unsubscribe from this group, send email to >> sqlalchemy+unsubscr...@googlegroups.com. >> For more options, visit this group at >> http://groups.google.com/group/sqlalchemy?hl=en. >> > > -- > You received this message because you are subscribed to the Google Groups > "sqlalchemy" group. > To post to this group, send email to sqlalch...@googlegroups.com. > To unsubscribe from this group, send email to > sqlalchemy+unsubscr...@googlegroups.com. > For more options, visit this group at > http://groups.google.com/group/sqlalchemy?hl=en. > > -- You received this message because you are subscribed to the Google Groups "sqlalchemy" group. To post to this group, send email to sqlalch...@googlegroups.com. To unsubscribe from this group, send email to sqlalchemy+unsubscr...@googlegroups.com. For more options, visit this group at http://groups.google.com/group/sqlalchemy?hl=en.