diff -r 40208f18c7c2 -r de20b0903d7d web/views/editcontroller.py --- a/web/views/editcontroller.py Wed Nov 25 16:28:41 2015 +0100 +++ b/web/views/editcontroller.py Fri Dec 04 14:56:20 2015 +0100 @@ -27,7 +27,7 @@ from rql.utils import rqlvar_maker -from cubicweb import Binary, ValidationError +from cubicweb import Binary, ValidationError, neg_role from cubicweb.view import EntityAdapter from cubicweb.predicates import is_instance from cubicweb.web import (INTERNAL_FIELD_VALUE, RequestError, NothingToEdit, @@ -186,6 +186,7 @@ # deserves special treatment req.data['pending_inlined'] = defaultdict(set) req.data['pending_others'] = set() + req.data['pending_composite_delete'] = set() try: for formparams in self._ordered_formparams(): eid = self.edit_entity(formparams) @@ -204,6 +205,13 @@ # then execute rql to set all relations for querydef in self.relations_rql: self._cw.execute(*querydef) + # delete pending composite + for entity, rtype, role, targettype in req.data['pending_composite_delete']: + # Check the composite relation was not re-set in the same form + # (see test_reparent_subentity in web/test/unittest_application.py) + entity.cw_clear_relation_cache(rtype, role) + if not entity.related(rtype, role=role, targettypes=(targettype,)): + entity.cw_delete() # XXX this processes *all* pending operations of *all* entities if '__delete' in req.form: todelete = req.list_form_param('__delete', req.form, pop=True) @@ -303,6 +311,12 @@ if value is None or value == origvalues: continue # not edited / not modified / to do later + unlinked_eids = origvalues - value + if unlinked_eids: + # Special handling of composite relation removal + self.handle_composite_removal( + form, field, unlinked_eids) + if rschema.inlined and rqlquery is not None and field.role == 'subject': self.handle_inlined_relation(form, field, value, origvalues, rqlquery) elif form.edited_entity.has_eid(): @@ -313,6 +327,34 @@ except ProcessFormError as exc: self.errors.append((field, exc)) + def handle_composite_removal(self, form, field, removed_values): + """ + In EditController-handled forms, when the user removes a composite + relation, it triggers the removal of the related entity in the + composite. This is where this happens. + + See for instance test_subject_subentity_removal in + web/test/unittest_application.py. + """ + rschema = self._cw.vreg.schema.rschema(field.name) + for unlinked_eid in removed_values: + unlinked_entity = self._cw.entity_from_eid(unlinked_eid) + rdef = rschema.role_rdef(form.edited_entity.cw_etype, + unlinked_entity.cw_etype, + field.role) + if rdef.composite is not None: + if rdef.composite == field.role: + targettype = form.edited_entity.e_schema + to_be_removed = unlinked_entity + else: + targettype = unlinked_entity.e_schema + to_be_removed = form.edited_entity + self.info('Scheduling removal of %s as composite relation ' + '%s was removed', to_be_removed, rdef) + form._cw.data['pending_composite_delete'].add( + (to_be_removed, field.name, + neg_role(rdef.composite), targettype)) + def handle_inlined_relation(self, form, field, values, origvalues, rqlquery): """handle edition for the (rschema, x) relation of the given entity """