From 4161271a8c44c2392d8fc09a91391d6b0619dc9c Mon Sep 17 00:00:00 2001 From: Mike Bayer Date: Fri, 2 Nov 2007 17:58:20 +0000 Subject: [PATCH] - merge() includes a keyword argument "dont_load=True". setting this flag will cause the merge operation to not load any data from the database in response to incoming detached objects, and will accept the incoming detached object as though it were already present in that session. Use this to merge detached objects from external caching systems into the session. --- CHANGES | 6 ++++ lib/sqlalchemy/orm/properties.py | 8 ++--- lib/sqlalchemy/orm/session.py | 9 ++++-- test/orm/merge.py | 53 +++++++++++++++++++++----------- 4 files changed, 52 insertions(+), 24 deletions(-) diff --git a/CHANGES b/CHANGES index 81adb1e8e1..c04dfe8ed6 100644 --- a/CHANGES +++ b/CHANGES @@ -81,6 +81,12 @@ CHANGES e.g. if you pickle a series of objects and unpickle (i.e. as in a Pylons HTTP session or similar), they can go into a new session without any conflict + - merge() includes a keyword argument "dont_load=True". setting this flag will cause + the merge operation to not load any data from the database in response to incoming + detached objects, and will accept the incoming detached object as though it were + already present in that session. Use this to merge detached objects from external + caching systems into the session. + 0.4.0 ----- diff --git a/lib/sqlalchemy/orm/properties.py b/lib/sqlalchemy/orm/properties.py index 6967b5e350..1e6be1f7df 100644 --- a/lib/sqlalchemy/orm/properties.py +++ b/lib/sqlalchemy/orm/properties.py @@ -61,7 +61,7 @@ class ColumnProperty(StrategizedProperty): def get_history(self, obj, passive=False): return sessionlib.attribute_manager.get_history(obj, self.key, passive=passive) - def merge(self, session, source, dest, _recursive): + def merge(self, session, source, dest, dont_load, _recursive): setattr(dest, self.key, getattr(source, self.key, None)) def get_col_value(self, column, value): @@ -280,7 +280,7 @@ class PropertyLoader(StrategizedProperty): def __str__(self): return str(self.parent.class_.__name__) + "." + self.key + " (" + str(self.mapper.class_.__name__) + ")" - def merge(self, session, source, dest, _recursive): + def merge(self, session, source, dest, dont_load, _recursive): if not "merge" in self.cascade or self.mapper in _recursive: return childlist = sessionlib.attribute_manager.get_history(source, self.key, passive=True) @@ -290,14 +290,14 @@ class PropertyLoader(StrategizedProperty): # sets a blank collection according to the correct list class dest_list = sessionlib.attribute_manager.init_collection(dest, self.key) for current in list(childlist): - obj = session.merge(current, entity_name=self.mapper.entity_name, _recursive=_recursive) + obj = session.merge(current, entity_name=self.mapper.entity_name, dont_load=dont_load, _recursive=_recursive) if obj is not None: #dest_list.append_without_event(obj) dest_list.append_with_event(obj) else: current = list(childlist)[0] if current is not None: - obj = session.merge(current, entity_name=self.mapper.entity_name, _recursive=_recursive) + obj = session.merge(current, entity_name=self.mapper.entity_name, dont_load=dont_load, _recursive=_recursive) if obj is not None: setattr(dest, self.key, obj) diff --git a/lib/sqlalchemy/orm/session.py b/lib/sqlalchemy/orm/session.py index 6be72ecefe..30e97eba7c 100644 --- a/lib/sqlalchemy/orm/session.py +++ b/lib/sqlalchemy/orm/session.py @@ -837,7 +837,7 @@ class Session(object): self._delete_impl(c, ignore_transient=True) - def merge(self, object, entity_name=None, _recursive=None): + def merge(self, object, entity_name=None, dont_load=False, _recursive=None): """Copy the state of the given `object` onto the persistent object with the same identifier. @@ -868,12 +868,17 @@ class Session(object): else: if key in self.identity_map: merged = self.identity_map[key] + elif dont_load: + merged = attribute_manager.new_instance(mapper.class_) + merged._instance_key = key + self.update(merged, entity_name=mapper.entity_name) + merged._state.committed_state = object._state.committed_state.copy() else: merged = self.get(mapper.class_, key[1]) if merged is None: raise exceptions.AssertionError("Instance %s has an instance key but is not persisted" % mapperutil.instance_str(object)) for prop in mapper.iterate_properties: - prop.merge(self, object, merged, _recursive) + prop.merge(self, object, merged, dont_load, _recursive) if key is None: self.save(merged, entity_name=mapper.entity_name) return merged diff --git a/test/orm/merge.py b/test/orm/merge.py index 3dd0a95a47..7f45c56a2d 100644 --- a/test/orm/merge.py +++ b/test/orm/merge.py @@ -101,6 +101,22 @@ class MergeTest(AssertMixin): u = sess.query(User).get(7) self.assert_result([u], User, {'user_id':7, 'user_name':'fred2', 'addresses':(Address, [{'email_address':'foo@bar.com'}, {'email_address':'hoho@lalala.com'}])}) + # merge persistent object into another session + sess4 = create_session() + u = sess4.merge(u) + def go(): + sess4.flush() + # no changes; therefore flush should do nothing + self.assert_sql_count(testbase.db, go, 0) + + # test with "dontload" merge + sess5 = create_session() + u = sess5.merge(u, dont_load=True) + def go(): + sess5.flush() + # no changes; therefore flush should do nothing + self.assert_sql_count(testbase.db, go, 0) + def test_saved_cascade_2(self): """tests a more involved merge""" mapper(Order, orders, properties={ @@ -141,27 +157,28 @@ class MergeTest(AssertMixin): assert o2.customer.user_name == 'also fred' def test_saved_cascade_3(self): - """test merge of a persistent entity with one_to_one relationship""" - mapper(User, users, properties={ - 'address':relation(mapper(Address, addresses),uselist = False) - }) - sess = create_session() - u = User() - u.user_id = 7 - u.user_name = "fred" - a1 = Address() - a1.email_address='foo@bar.com' - u.address = a1 + """test merge of a persistent entity with one_to_one relationship""" - sess.save(u) - sess.flush() + mapper(User, users, properties={ + 'address':relation(mapper(Address, addresses),uselist = False) + }) + sess = create_session() + u = User() + u.user_id = 7 + u.user_name = "fred" + a1 = Address() + a1.email_address='foo@bar.com' + u.address = a1 - sess2 = create_session() - u2 = sess2.query(User).get(7) - u2.user_name = 'fred2' - u2.address.email_address = 'hoho@lalala.com' + sess.save(u) + sess.flush() + + sess2 = create_session() + u2 = sess2.query(User).get(7) + u2.user_name = 'fred2' + u2.address.email_address = 'hoho@lalala.com' - u3 = sess.merge(u2) + u3 = sess.merge(u2) if __name__ == "__main__": testbase.main() -- 2.47.3