]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
- fixed "cascade delete" operation of dynamic relations,
authorMike Bayer <mike_mp@zzzcomputing.com>
Sun, 16 Mar 2008 23:49:55 +0000 (23:49 +0000)
committerMike Bayer <mike_mp@zzzcomputing.com>
Sun, 16 Mar 2008 23:49:55 +0000 (23:49 +0000)
which had only been implemented for foreign-key nulling
behavior in 0.4.2 and not actual cascading deletes
[ticket:895]

CHANGES
lib/sqlalchemy/orm/attributes.py
lib/sqlalchemy/orm/dynamic.py
test/orm/dynamic.py

diff --git a/CHANGES b/CHANGES
index 006849a420c293ed5c50d3708b72bd1c189c40a8..00c5485e2cffb154364558f1190ba0b504f293f9 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -16,7 +16,12 @@ CHANGES
       from being used with inheritance
 
     - Session.execute can now find binds from metadata
-
+    
+    - fixed "cascade delete" operation of dynamic relations,
+      which had only been implemented for foreign-key nulling
+      behavior in 0.4.2 and not actual cascading deletes
+      [ticket:895]
+      
 - extensions
     - the "synonym" function is now directly usable with 
       "declarative".  Pass in the decorated property using 
index 3173f82740d8810bf3e0a930991ac4f25e686693..b0c2ac8261cab2e482e4a4f39bab1dcbb93e904f 100644 (file)
@@ -1160,7 +1160,7 @@ def get_as_list(state, key, passive=False):
     if x is PASSIVE_NORESULT:
         return None
     elif hasattr(attr, 'get_collection'):
-        return attr.get_collection(state, x)
+        return attr.get_collection(state, x, passive=passive)
     elif isinstance(x, list):
         return x
     else:
index 0d72beddb6356339470c62fe84f5c054fbec6863..09de60db2dab25f04c6fa669785cfe3616918b41 100644 (file)
@@ -14,12 +14,16 @@ class DynamicAttributeImpl(attributes.AttributeImpl):
 
     def get(self, state, passive=False):
         if passive:
-            return self._get_collection(state, passive=True).added_items
+            return self._get_collection_history(state, passive=True).added_items
         else:
             return AppenderQuery(self, state)
 
-    def get_collection(self, state, user_data=None):
-        return self._get_collection(state, passive=True).added_items
+    def get_collection(self, state, user_data=None, passive=True):
+        if passive:
+            return self._get_collection_history(state, passive=passive).added_items
+        else:
+            history = self._get_collection_history(state, passive=passive)
+            return history.added_items + history.unchanged_items
 
     def fire_append_event(self, state, value, initiator):
         state.modified = True
@@ -53,10 +57,10 @@ class DynamicAttributeImpl(attributes.AttributeImpl):
         raise NotImplementedError()
         
     def get_history(self, state, passive=False):
-        c = self._get_collection(state, passive)
+        c = self._get_collection_history(state, passive)
         return (c.added_items, c.unchanged_items, c.deleted_items)
         
-    def _get_collection(self, state, passive=False):
+    def _get_collection_history(self, state, passive=False):
         try:
             c = state.dict[self.key]
         except KeyError:
@@ -69,12 +73,12 @@ class DynamicAttributeImpl(attributes.AttributeImpl):
         
     def append(self, state, value, initiator, passive=False):
         if initiator is not self:
-            self._get_collection(state, passive=True).added_items.append(value)
+            self._get_collection_history(state, passive=True).added_items.append(value)
             self.fire_append_event(state, value, initiator)
     
     def remove(self, state, value, initiator, passive=False):
         if initiator is not self:
-            self._get_collection(state, passive=True).deleted_items.append(value)
+            self._get_collection_history(state, passive=True).deleted_items.append(value)
             self.fire_remove_event(state, value, initiator)
 
             
@@ -100,21 +104,21 @@ class AppenderQuery(Query):
     def __iter__(self):
         sess = self.__session()
         if sess is None:
-            return iter(self.attr._get_collection(self.instance._state, passive=True).added_items)
+            return iter(self.attr._get_collection_history(self.instance._state, passive=True).added_items)
         else:
             return iter(self._clone(sess))
 
     def __getitem__(self, index):
         sess = self.__session()
         if sess is None:
-            return self.attr._get_collection(self.instance._state, passive=True).added_items.__getitem__(index)
+            return self.attr._get_collection_history(self.instance._state, passive=True).added_items.__getitem__(index)
         else:
             return self._clone(sess).__getitem__(index)
     
     def count(self):
         sess = self.__session()
         if sess is None:
-            return len(self.attr._get_collection(self.instance._state, passive=True).added_items)
+            return len(self.attr._get_collection_history(self.instance._state, passive=True).added_items)
         else:
             return self._clone(sess).count()
     
@@ -142,7 +146,7 @@ class AppenderQuery(Query):
             oldlist = list(self)
         else:
             oldlist = []
-        self.attr._get_collection(self.instance._state, passive=True).replace(oldlist, collection)
+        self.attr._get_collection_history(self.instance._state, passive=True).replace(oldlist, collection)
         return oldlist
         
     def append(self, item):
index ae8ef4e5ea3fffcc880a7296ee1559db2e2ab9d3..2b5c0eeecb701580e8169a9777caccb22439fd13 100644 (file)
@@ -121,7 +121,7 @@ class FlushTest(FixtureTest):
         ] == sess.query(User).all()
 
     @testing.fails_on('maxdb')
-    def test_delete(self):
+    def test_delete_nocascade(self):
         mapper(User, users, properties={
             'addresses':dynamic_loader(mapper(Address, addresses), backref='user')
         })
@@ -141,6 +141,9 @@ class FlushTest(FixtureTest):
         sess.delete(u.addresses[3])
         assert [Address(email_address='a'), Address(email_address='b'), Address(email_address='d')] == list(u.addresses)
 
+        sess.clear()
+        u = sess.query(User).get(u.id)
+
         sess.delete(u)
 
         # u.addresses relation will have to force the load
@@ -150,6 +153,39 @@ class FlushTest(FixtureTest):
 
         assert testing.db.scalar(addresses.count(addresses.c.user_id != None)) ==0
 
+    @testing.fails_on('maxdb')
+    def test_delete_cascade(self):
+        mapper(User, users, properties={
+            'addresses':dynamic_loader(mapper(Address, addresses), backref='user', cascade="all, delete-orphan")
+        })
+        sess = create_session(autoflush=True)
+        u = User(name='ed')
+        u.addresses.append(Address(email_address='a'))
+        u.addresses.append(Address(email_address='b'))
+        u.addresses.append(Address(email_address='c'))
+        u.addresses.append(Address(email_address='d'))
+        u.addresses.append(Address(email_address='e'))
+        u.addresses.append(Address(email_address='f'))
+        sess.save(u)
+
+        assert Address(email_address='c') == u.addresses[2]
+        sess.delete(u.addresses[2])
+        sess.delete(u.addresses[4])
+        sess.delete(u.addresses[3])
+        assert [Address(email_address='a'), Address(email_address='b'), Address(email_address='d')] == list(u.addresses)
+        
+        sess.clear()
+        u = sess.query(User).get(u.id)
+        
+        sess.delete(u)
+
+        # u.addresses relation will have to force the load
+        # of all addresses so that they can be updated
+        sess.flush()
+        sess.close()
+
+        assert testing.db.scalar(addresses.count()) ==0
+
     @testing.fails_on('maxdb')
     def test_remove_orphans(self):
         mapper(User, users, properties={