[itree / hooks] Keep ordering sequential on child removal
authorSylvain Thénault <sylvain.thenault@logilab.fr>
Mon, 11 Dec 2017 15:13:00 +0100
changeset 2909 7042ff92abcf
parent 2908 e08c4e3177f5
child 2910 8a08e1df2e59
[itree / hooks] Keep ordering sequential on child removal because we have to keep this property in the targeted ordering implementation.
cubicweb_seda/entities/itree.py
cubicweb_seda/hooks.py
test/test_hooks.py
--- a/cubicweb_seda/entities/itree.py	Mon Dec 11 14:11:50 2017 +0100
+++ b/cubicweb_seda/entities/itree.py	Mon Dec 11 15:13:00 2017 +0100
@@ -216,6 +216,19 @@
     child.cw_set(ordering=order)
 
 
+def prepare_child_removal(child):
+    """Before `child` will be removed or reparented, update its former parent's
+    child to keep their `ordering` attribute sequential.
+
+    This is expected to be called by a hook.
+    """
+    rtype = ETYPE_PARENT_RTYPE[child.cw_etype]
+    child._cw.execute(
+        'SET C ordering CO - 1 WHERE C ordering CO, C ordering > XO, '
+        'X ordering XO, X {rtype} P, C {rtype} P, X eid %(x)s'.format(rtype=rtype),
+        {'x': child.eid}).rows
+
+
 def reparent(entity, new_parent_eid):
     """Move `entity` as a children of `new_parent_eid`.
     """
--- a/cubicweb_seda/hooks.py	Mon Dec 11 14:11:50 2017 +0100
+++ b/cubicweb_seda/hooks.py	Mon Dec 11 15:13:00 2017 +0100
@@ -27,7 +27,7 @@
 
 from .xsd2yams import MULTIPLE_CHILDREN
 from .entities import rule_type_from_etype, diag
-from .entities.itree import next_child_ordering
+from .entities.itree import next_child_ordering, prepare_child_removal
 from .entities.generated import (CHOICE_RTYPE,
                                  CHECK_CARD_ETYPES, CHECK_CHILDREN_CARD_RTYPES)
 
@@ -521,7 +521,21 @@
         eid, parent_eid = self.eidfrom, self.eidto
         ordering = next_child_ordering(self._cw, parent_eid, self.rtype)
         entity = self._cw.entity_from_eid(eid)
-        entity.cw_set(ordering=ordering + 1)
+        entity.cw_set(ordering=ordering)
+
+
+class KeepOrderingSequentielHook(hook.Hook):
+    """Hook to keep `ordering` attribute of sibbling entities sequential when some
+    relation will be removed.
+    """
+    __regid__ = 'seda.ordering.remove'
+    __select__ = hook.Hook.__select__ & hook.match_rtype_sets(
+        {rtype for _, rtype in MULTIPLE_CHILDREN})
+
+    events = ('before_delete_relation',)
+
+    def __call__(self):
+        prepare_child_removal(self._cw.entity_from_eid(self.eidfrom))
 
 
 def registration_callback(vreg):
--- a/test/test_hooks.py	Mon Dec 11 14:11:50 2017 +0100
+++ b/test/test_hooks.py	Mon Dec 11 15:13:00 2017 +0100
@@ -306,7 +306,7 @@
         self.assertEqual(format_ids, set(expected_format_ids))
 
 
-class SetOrderingHooksTC(CubicWebTC):
+class OrderingHooksTC(CubicWebTC):
 
     def test_first_level_archive_units(self):
         with self.admin_access.cnx() as cnx:
@@ -345,6 +345,19 @@
             self.assertEqual(bdo1.reverse_seda_data_object_reference_id[0].ordering, 1)
             self.assertEqual(bdo2.reverse_seda_data_object_reference_id[0].ordering, 2)
 
+    def test_remove_keep_in_sync(self):
+        with self.admin_access.cnx() as cnx:
+            transfer = cnx.create_entity('SEDAArchiveTransfer', title=u'test profile')
+            bdo1 = testutils.create_data_object(transfer)
+            bdo2 = testutils.create_data_object(transfer)
+            cnx.commit()
+
+            bdo1.cw_delete()
+            cnx.commit()
+
+            bdo2.cw_clear_all_caches()
+            self.assertEqual(bdo2.ordering, 1)
+
 
 if __name__ == '__main__':
     import unittest