From: Ants Aasma Date: Wed, 31 Oct 2007 16:30:06 +0000 (+0000) Subject: - also added proxying of save_or_update to scoped sessions. X-Git-Tag: rel_0_4_1~87 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=5a1863d092e07ae1d052af54eb0ac7a46400e399;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git - also added proxying of save_or_update to scoped sessions. - session.update() raises an error when updating an instance that is already in the session with a different identity - adjusted zoomarks lower limits so I can get a nice clean run --- diff --git a/CHANGES b/CHANGES index 333589cc81..00d16e97c1 100644 --- a/CHANGES +++ b/CHANGES @@ -59,7 +59,11 @@ CHANGES - Fixed a truncation error when re-assigning a subset of a collection (obj.relation = obj.relation[1:]) [ticket:834] -- Added proxying of __contains__ and __iter__ methods for scoped sessions. +- Added proxying of save_or_update, __contains__ and __iter__ methods for + scoped sessions. + +- session.update() raises an error when updating an instance that is already + in the session with a different identity. 0.4.0 ----- diff --git a/lib/sqlalchemy/orm/scoping.py b/lib/sqlalchemy/orm/scoping.py index 3bee9b0e9a..82692aad26 100644 --- a/lib/sqlalchemy/orm/scoping.py +++ b/lib/sqlalchemy/orm/scoping.py @@ -73,7 +73,7 @@ def instrument(name): def do(self, *args, **kwargs): return getattr(self.registry(), name)(*args, **kwargs) return do -for meth in ('get', 'load', 'close', 'save', 'commit', 'update', 'flush', 'query', 'delete', 'merge', 'clear', 'refresh', 'expire', 'expunge', 'rollback', 'begin', 'begin_nested', 'connection', 'execute', 'scalar', 'get_bind', 'is_modified', '__contains__', '__iter__'): +for meth in ('get', 'load', 'close', 'save', 'commit', 'update', 'save_or_update', 'flush', 'query', 'delete', 'merge', 'clear', 'refresh', 'expire', 'expunge', 'rollback', 'begin', 'begin_nested', 'connection', 'execute', 'scalar', 'get_bind', 'is_modified', '__contains__', '__iter__'): setattr(ScopedSession, meth, instrument(meth)) def makeprop(name): diff --git a/lib/sqlalchemy/orm/session.py b/lib/sqlalchemy/orm/session.py index 42ad30b6a2..e39062b2cd 100644 --- a/lib/sqlalchemy/orm/session.py +++ b/lib/sqlalchemy/orm/session.py @@ -800,8 +800,9 @@ class Session(object): """Bring the given detached (saved) instance into this ``Session``. - If there is a persistent instance with the same identifier - already associated with this ``Session``, an exception is thrown. + If there is a persistent instance with the same instance key, but + different identity already associated with this ``Session``, an + InvalidRequestError exception is thrown. This operation cascades the `save_or_update` method to associated instances if the relation is mapped with @@ -969,6 +970,8 @@ class Session(object): return if not hasattr(obj, '_instance_key'): raise exceptions.InvalidRequestError("Instance '%s' is not persisted" % mapperutil.instance_str(obj)) + elif self.identity_map.get(obj._instance_key, obj) is not obj: + raise exceptions.InvalidRequestError("Instance '%s' is with key %s already persisted with a different identity" % (mapperutil.instance_str(obj), obj._instance_key)) self._attach(obj) def _register_persistent(self, obj): diff --git a/test/orm/assorted_eager.py b/test/orm/assorted_eager.py index ad95a0a492..25a848976f 100644 --- a/test/orm/assorted_eager.py +++ b/test/orm/assorted_eager.py @@ -652,7 +652,7 @@ class EagerTest7(ORMTest): # set up an invoice i1 = Invoice() i1.date = datetime.datetime.now() - i1.company = c1 + i1.company = a item1 = Item() item1.code = 'aaaa' diff --git a/test/orm/session.py b/test/orm/session.py index 6efc93d50e..530380b810 100644 --- a/test/orm/session.py +++ b/test/orm/session.py @@ -681,7 +681,43 @@ class SessionTest(AssertMixin): log = [] sess.commit() assert log == ['before_commit', 'after_commit'] + + def test_duplicate_update(self): + mapper(User, users) + Session = sessionmaker() + sess = Session() + + u1 = User() + sess.save(u1) + sess.flush() + assert u1.user_id is not None + + sess.expunge(u1) + + assert u1 not in sess + + u2 = sess.query(User).get(u1.user_id) + assert u2 is not None and u2 is not u1 + assert u2 in sess + self.assertRaises(Exception, lambda: sess.update(u1)) + + sess.expunge(u2) + assert u2 not in sess + + u1.user_name = "John" + u2.user_name = "Doe" + + sess.update(u1) + assert u1 in sess + + sess.flush() + + sess.clear() + + u3 = sess.query(User).get(u1.user_id) + assert u3 is not u1 and u3 is not u2 and u3.user_name == u1.user_name + class ScopedSessionTest(ORMTest): def define_tables(self, metadata): diff --git a/test/orm/unitofwork.py b/test/orm/unitofwork.py index 74b08e2c8b..7669d25541 100644 --- a/test/orm/unitofwork.py +++ b/test/orm/unitofwork.py @@ -1025,7 +1025,8 @@ class SaveTest(ORMTest): # check it again, identity should be different but ids the same nu = Session.get(m, u.user_id) self.assert_(u is not nu and u.user_id == nu.user_id and nu.user_name == 'savetester') - + Session.close() + # change first users name and save Session.update(u) u.user_name = 'modifiedname' diff --git a/test/profiling/zoomark.py b/test/profiling/zoomark.py index 5c22fd9d80..58033ad976 100644 --- a/test/profiling/zoomark.py +++ b/test/profiling/zoomark.py @@ -50,7 +50,7 @@ class ZooMarkTest(testing.AssertMixin): metadata.create_all() @testing.supported('postgres') - @profiling.profiled('populate', call_range=(3580, 4400), always=True) + @profiling.profiled('populate', call_range=(3360, 4400), always=True) def test_1a_populate(self): Zoo = metadata.tables['Zoo'] Animal = metadata.tables['Animal'] @@ -118,7 +118,7 @@ class ZooMarkTest(testing.AssertMixin): MotherID=bai_yun) @testing.supported('postgres') - @profiling.profiled('insert', call_range=(195, 250), always=True) + @profiling.profiled('insert', call_range=(180, 250), always=True) def test_2_insert(self): Animal = metadata.tables['Animal'] i = Animal.insert() @@ -126,7 +126,7 @@ class ZooMarkTest(testing.AssertMixin): tick = i.execute(Species='Tick', Name='Tick %d' % x, Legs=8) @testing.supported('postgres') - @profiling.profiled('properties', call_range=(3080, 3430), always=True) + @profiling.profiled('properties', call_range=(3060, 3430), always=True) def test_3_properties(self): Zoo = metadata.tables['Zoo'] Animal = metadata.tables['Animal'] @@ -149,7 +149,7 @@ class ZooMarkTest(testing.AssertMixin): ticks = fullobject(Animal.select(Animal.c.Species=='Tick')) @testing.supported('postgres') - @profiling.profiled('expressions', call_range=(11600, 13200), always=True) + @profiling.profiled('expressions', call_range=(11450, 13200), always=True) def test_4_expressions(self): Zoo = metadata.tables['Zoo'] Animal = metadata.tables['Animal'] @@ -203,7 +203,7 @@ class ZooMarkTest(testing.AssertMixin): assert len(fulltable(Animal.select(func.date_part('day', Animal.c.LastEscape) == 21))) == 1 @testing.supported('postgres') - @profiling.profiled('aggregates', call_range=(1050, 1270), always=True) + @profiling.profiled('aggregates', call_range=(1020, 1270), always=True) def test_5_aggregates(self): Animal = metadata.tables['Animal'] Zoo = metadata.tables['Zoo'] @@ -245,7 +245,7 @@ class ZooMarkTest(testing.AssertMixin): legs.sort() @testing.supported('postgres') - @profiling.profiled('editing', call_range=(1330, 1390), always=True) + @profiling.profiled('editing', call_range=(1300, 1390), always=True) def test_6_editing(self): Zoo = metadata.tables['Zoo'] @@ -274,7 +274,7 @@ class ZooMarkTest(testing.AssertMixin): assert SDZ['Founded'] == datetime.date(1935, 9, 13) @testing.supported('postgres') - @profiling.profiled('multiview', call_range=(2870, 3155), always=True) + @profiling.profiled('multiview', call_range=(2850, 3155), always=True) def test_7_multiview(self): Zoo = metadata.tables['Zoo'] Animal = metadata.tables['Animal'] diff --git a/test/zblog/tests.py b/test/zblog/tests.py index ad6876937d..359e8370b7 100644 --- a/test/zblog/tests.py +++ b/test/zblog/tests.py @@ -79,6 +79,7 @@ class SavePostTest(ZBlogTest): s.flush() s.clear() + user = s.query(User).get(user_id) blog = s.query(Blog).get(blog_id) post = blog.posts[0] comment = Comment(subject="some subject", body="some body")