]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
- Load of a deferred() attribute on an object
authorMike Bayer <mike_mp@zzzcomputing.com>
Sun, 31 Jul 2011 21:32:07 +0000 (17:32 -0400)
committerMike Bayer <mike_mp@zzzcomputing.com>
Sun, 31 Jul 2011 21:32:07 +0000 (17:32 -0400)
where row can't be located raises
ObjectDeletedError instead of failing later
on; improved the message in ObjectDeletedError
to include other conditions besides a simple
"delete". [ticket:2191]
- break up test_get_refreshes() in test_expire

CHANGES
lib/sqlalchemy/orm/exc.py
lib/sqlalchemy/orm/mapper.py
lib/sqlalchemy/orm/strategies.py
test/orm/test_expire.py

diff --git a/CHANGES b/CHANGES
index 1dcebbb87938c0f8fec9de8db1c37c2059929c14..ec66db0c15333bc717815ac7c417aacc018b0754 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -55,6 +55,13 @@ CHANGES
     warning, which is being considered for 0.8.
     [ticket:2205]
 
+  - Load of a deferred() attribute on an object
+    where row can't be located raises 
+    ObjectDeletedError instead of failing later
+    on; improved the message in ObjectDeletedError
+    to include other conditions besides a simple
+    "delete". [ticket:2191]
+
   - Fixed regression from 0.6 where a get history
     operation on some relationship() based attributes
     would fail when a lazyload would emit; this could 
index 1812f0d7209a285e14cd71fefaddffb4beaf01f6..98b97059eaae03de000181865739d7a539b7459e 100644 (file)
@@ -7,7 +7,7 @@
 """SQLAlchemy ORM exceptions."""
 
 import sqlalchemy as sa
-
+orm_util = sa.util.importlater('sqlalchemy.orm', 'util')
 
 NO_STATE = (AttributeError, KeyError)
 """Exception types that may be raised by instrumentation implementations."""
@@ -95,7 +95,12 @@ class ObjectDeletedError(sa.exc.InvalidRequestError):
     object.   
     
     """
-
+    def __init__(self, state):
+        sa.exc.InvalidRequestError.__init__(
+             self,
+             "Instance '%s' has been deleted, or its "
+             "row is otherwise not present." % orm_util.state_str(state)
+        )
 
 class UnmappedColumnError(sa.exc.InvalidRequestError):
     """Mapping operation was requested on an unknown column."""
index 3c3389d797de09585333ecea0daa6ab6a569aa99..becc470521929e9adf8ab3338d11aedd30e5de0c 100644 (file)
@@ -1586,9 +1586,7 @@ class Mapper(object):
         # if instance is pending, a refresh operation 
         # may not complete (even if PK attributes are assigned)
         if has_key and result is None:
-            raise orm_exc.ObjectDeletedError(
-                                "Instance '%s' has been deleted." % 
-                                state_str(state))
+            raise orm_exc.ObjectDeletedError(state)
 
     def _optimized_get_statement(self, state, attribute_names):
         """assemble a WHERE clause which retrieves a given state by primary
index aacf466d62e228cce660924fff2e2481b21b3673..c335d1509af82b68a122bca9e958ebd5fc541976 100644 (file)
@@ -242,8 +242,10 @@ class DeferredColumnLoader(LoaderStrategy):
                 )
 
         query = session.query(localparent)
-        query._load_on_ident(state.key, 
-                    only_load_props=group, refresh_state=state)
+        if query._load_on_ident(state.key, 
+                    only_load_props=group, refresh_state=state) is None:
+            raise orm_exc.ObjectDeletedError(state)
+
         return attributes.ATTR_WAS_SET
 
 log.class_logger(DeferredColumnLoader)
index 886af0d26514612fff9330d3fae024866660316a..57bd2c8cc843175eebf08fb1ebb77d79a5594172 100644 (file)
@@ -88,6 +88,13 @@ class ExpireTest(_fixtures.FixtureTest):
             u = s.query(User).get(10)  # expire flag reset, so not expired
         self.assert_sql_count(testing.db, go, 0)
 
+    def test_get_on_deleted_expunges(self):
+        users, User = self.tables.users, self.classes.User
+
+        mapper(User, users)
+        s = create_session(autocommit=False)
+        u = s.query(User).get(10)
+
         s.expire_all()
         s.execute(users.delete().where(User.id==10))
 
@@ -96,33 +103,54 @@ class ExpireTest(_fixtures.FixtureTest):
         assert s.query(User).get(10) is None
         assert u not in s # and expunges
 
-        # trick the "deleted" flag so we can re-add for the sake
-        # of this test
-        del attributes.instance_state(u).deleted
+    def test_refresh_on_deleted_raises(self):
+        users, User = self.tables.users, self.classes.User
 
-        # add it back
-        s.add(u)
-        # nope, raises ObjectDeletedError
-        assert_raises(sa.orm.exc.ObjectDeletedError, getattr, u, 'name')
+        mapper(User, users)
+        s = create_session(autocommit=False)
+        u = s.query(User).get(10)
+        s.expire_all()
+
+        s.expire_all()
+        s.execute(users.delete().where(User.id==10))
+
+        # raises ObjectDeletedError
+        assert_raises_message(
+            sa.orm.exc.ObjectDeletedError,
+            "Instance '<User at .*?>' has been "
+            "deleted, or its row is otherwise not present.",
+            getattr, u, 'name'
+        )
+
+    def test_rollback_undoes_expunge_from_deleted(self):
+        users, User = self.tables.users, self.classes.User
 
-        # do a get()/remove u from session again
+        mapper(User, users)
+        s = create_session(autocommit=False)
+        u = s.query(User).get(10)
+        s.expire_all()
+        s.execute(users.delete().where(User.id==10))
+
+        # do a get()/remove u from session
         assert s.query(User).get(10) is None
         assert u not in s
 
         s.rollback()
 
         assert u in s
-        # but now its back, rollback has occurred, the _remove_newly_deleted
-        # is reverted
+        # but now its back, rollback has occurred, the 
+        # _remove_newly_deleted is reverted
         eq_(u.name, 'chuck')
 
     def test_deferred(self):
-        """test that unloaded, deferred attributes aren't included in the expiry list."""
+        """test that unloaded, deferred attributes aren't included in the 
+        expiry list."""
 
         Order, orders = self.classes.Order, self.tables.orders
 
 
-        mapper(Order, orders, properties={'description':deferred(orders.c.description)})
+        mapper(Order, orders, properties={
+                    'description':deferred(orders.c.description)})
 
         s = create_session()
         o1 = s.query(Order).first()
@@ -132,6 +160,23 @@ class ExpireTest(_fixtures.FixtureTest):
         assert 'description' not in o1.__dict__
         assert o1.description
 
+    def test_deferred_notfound(self):
+        Order, orders = self.classes.Order, self.tables.orders
+
+        mapper(Order, orders, properties={
+                'description':deferred(orders.c.description)})
+        s = create_session()
+        o1 = s.query(Order).first()
+        assert 'description' not in o1.__dict__
+        s.expire(o1)
+        s.query(Order).delete()
+        assert_raises_message(
+            sa.orm.exc.ObjectDeletedError,
+            "Instance '<Order at .*?>' has been "
+            "deleted, or its row is otherwise not present.",
+            getattr, o1, 'description'
+        )
+
     def test_lazyload_autoflushes(self):
         users, Address, addresses, User = (self.tables.users,
                                 self.classes.Address,
@@ -139,7 +184,8 @@ class ExpireTest(_fixtures.FixtureTest):
                                 self.classes.User)
 
         mapper(User, users, properties={
-            'addresses':relationship(Address, order_by=addresses.c.email_address)
+            'addresses':relationship(Address, 
+                    order_by=addresses.c.email_address)
         })
         mapper(Address, addresses)
         s = create_session(autoflush=True, autocommit=False)