Commits

Mike Bayer committed b1a08ca

fixed both group-deferred attributes and expired attributes to not
blow away changes made on attributes before the load takes place

Comments (0)

Files changed (4)

lib/sqlalchemy/orm/attributes.py

         self.callables[key] = callable_
 
     def __fire_trigger(self):
-        self.trigger(self.obj(), self.expired_attributes)
+        instance = self.obj()
+        self.trigger(instance, [k for k in self.expired_attributes if k not in self.dict])
         for k in self.expired_attributes:
             self.callables.pop(k, None)
         self.expired_attributes.clear()

lib/sqlalchemy/orm/strategies.py

             strategy = self.parent_property._get_strategy(DeferredColumnLoader)
             
             # full list of ColumnProperty objects to be loaded in the deferred fetch
-            props = [p for p in mapper.iterate_properties if isinstance(p.strategy, ColumnLoader) and p.columns[0].table in needs_tables]
+            props = [p.key for p in mapper.iterate_properties if isinstance(p.strategy, ColumnLoader) and p.columns[0].table in needs_tables]
 
             # TODO: we are somewhat duplicating efforts from mapper._get_poly_select_loader 
             # and should look for ways to simplify.
             if props is not None:
                 group = props
             elif self.group is not None:
-                group = [p for p in localparent.iterate_properties if isinstance(p.strategy, DeferredColumnLoader) and p.group==self.group]
+                group = [p.key for p in localparent.iterate_properties if isinstance(p.strategy, DeferredColumnLoader) and p.group==self.group]
             else:
-                group = [self.parent_property]
-                
+                group = [self.parent_property.key]
+            
+            group = [k for k in group if k not in instance.__dict__]
+            
             if self._should_log_debug:
                 self.logger.debug("deferred load %s group %s" % (mapperutil.attribute_str(instance, self.key), group and ','.join([p.key for p in group]) or 'None'))
 
 
             if create_statement is None:
                 ident = instance._instance_key[1]
-                session.query(localparent)._get(None, ident=ident, only_load_props=[p.key for p in group], refresh_instance=instance)
+                session.query(localparent)._get(None, ident=ident, only_load_props=group, refresh_instance=instance)
             else:
                 statement, params = create_statement(instance)
-                session.query(localparent).from_statement(statement).params(params)._get(None, only_load_props=[p.key for p in group], refresh_instance=instance)
+                session.query(localparent).from_statement(statement).params(params)._get(None, only_load_props=group, refresh_instance=instance)
             return attributes.ATTR_WAS_SET
         return lazyload
                 

test/orm/expire.py

         sess.flush()
         sess.clear()
         assert sess.query(User).get(7).name == 'somenewname'
+    
+    def test_expire_preserves_changes(self):
+        """test that the expire load operation doesn't revert post-expire changes"""
+        
+        mapper(Order, orders)
+        sess = create_session()
+        o = sess.query(Order).get(3)
+        sess.expire(o)
+        
+        o.description = "order 3 modified"
+        def go():
+            assert o.isopen == 1
+        self.assert_sql_count(testbase.db, go, 1)
+        assert o.description == 'order 3 modified'
         
     def test_expire_committed(self):
         """test that the committed state of the attribute receives the most recent DB data"""

test/orm/mapper.py

         def go():
             sess.flush()
         self.assert_sql_count(testbase.db, go, 0)
-
+    
+    def test_preserve_changes(self):
+        """test that the deferred load operation doesn't revert modifications on attributes"""
+        
+        mapper(Order, orders, properties = {
+            'userident':deferred(orders.c.user_id, group='primary'),
+            'description':deferred(orders.c.description, group='primary'),
+            'opened':deferred(orders.c.isopen, group='primary')
+        })
+        sess = create_session()
+        o = sess.query(Order).get(3)
+        assert 'userident' not in o.__dict__
+        o.description = 'somenewdescription'
+        assert o.description == 'somenewdescription'
+        def go():
+            assert o.opened == 1
+        self.assert_sql_count(testbase.db, go, 1)
+        assert o.description == 'somenewdescription'
+        assert o in sess.dirty
+        
+        
     def test_commitsstate(self):
         """test that when deferred elements are loaded via a group, they get the proper CommittedState
         and dont result in changes being committed"""