[Sqlalchemy-tickets] Issue #4187: post_update fails if the parent object is deleted and the child i
Brought to you by:
zzzeek
From: Michael B. <iss...@bi...> - 2018-02-12 21:31:12
|
New issue 4187: post_update fails if the parent object is deleted and the child isn't https://bitbucket.org/zzzeek/sqlalchemy/issues/4187/post_update-fails-if-the-parent-object-is Michael Bayer: it sounds unbelievable but it's true. a needless UPDATE is emitted that only recenly raises an error due to #3496, but it's always been there. nothing else is needed: ``` #!python from sqlalchemy import * from sqlalchemy.orm import * from sqlalchemy.ext.declarative import declarative_base Base = declarative_base() class User(Base): __tablename__ = 'user' id = Column(Integer, primary_key=True) preferred_address_id = Column(ForeignKey("address.id")) preferred_address = relationship( "Address", primaryjoin=lambda: User.preferred_address_id == Address.id, post_update=True ) class Address(Base): __tablename__ = 'address' id = Column(Integer, primary_key=True) e = create_engine("sqlite://", echo=True) Base.metadata.create_all(e) sess = Session(e) a1 = Address() bart = User(preferred_address=a1) sess.add(bart) sess.flush() sess.delete(bart) sess.flush() ``` ``` #!python Traceback (most recent call last): File "test.py", line 38, in <module> sess.flush() File "/home/classic/dev/sqlalchemy/lib/sqlalchemy/orm/session.py", line 2243, in flush self._flush(objects) File "/home/classic/dev/sqlalchemy/lib/sqlalchemy/orm/session.py", line 2369, in _flush transaction.rollback(_capture_exception=True) File "/home/classic/dev/sqlalchemy/lib/sqlalchemy/util/langhelpers.py", line 66, in __exit__ compat.reraise(exc_type, exc_value, exc_tb) File "/home/classic/dev/sqlalchemy/lib/sqlalchemy/util/compat.py", line 187, in reraise raise value File "/home/classic/dev/sqlalchemy/lib/sqlalchemy/orm/session.py", line 2333, in _flush flush_context.execute() File "/home/classic/dev/sqlalchemy/lib/sqlalchemy/orm/unitofwork.py", line 391, in execute rec.execute(self) File "/home/classic/dev/sqlalchemy/lib/sqlalchemy/orm/unitofwork.py", line 542, in execute persistence.post_update(self.mapper, states, uow, cols) File "/home/classic/dev/sqlalchemy/lib/sqlalchemy/orm/persistence.py", line 234, in post_update mapper, table, update) File "/home/classic/dev/sqlalchemy/lib/sqlalchemy/orm/persistence.py", line 982, in _emit_post_update_statements (table.description, len(records), rows)) sqlalchemy.orm.exc.StaleDataError: UPDATE statement on table 'user' expected to update 1 row(s); 0 were matched. ``` no actual relationship cycle is needed, just the two objects. the test_cycles test suite fails to test for this, here's a failing test: ``` #!diff diff --git a/test/orm/test_cycles.py b/test/orm/test_cycles.py index abd05067a..dc0be41bb 100644 --- a/test/orm/test_cycles.py +++ b/test/orm/test_cycles.py @@ -639,6 +639,28 @@ class OneToManyManyToOneTest(fixtures.MappedTest): sess.add(p) sess.flush() + def test_post_update_m2o_no_cascade(self): + person, ball, Ball, Person = (self.tables.person, + self.tables.ball, + self.classes.Ball, + self.classes.Person) + + mapper(Ball, ball) + mapper(Person, person, properties=dict( + favorite=relationship( + Ball, primaryjoin=person.c.favorite_ball_id == ball.c.id, + post_update=True))) + b = Ball(data='some data') + p = Person(data='some data') + p.favorite = b + sess = create_session() + sess.add(b) + sess.add(p) + sess.flush() + + sess.delete(p) + sess.flush() + def test_post_update_m2o(self): """A cycle between two rows, with a post_update on the many-to-one""" ``` hoping this can be for 1.1.x however I am worried we might need #1063 for this to work, though "don't emit UPDATE if the object was deleted" seems simple... |