web/views/editcontroller.py
changeset 11063 de20b0903d7d
parent 11061 c01325774d21
child 11064 113e9da47afc
equal deleted inserted replaced
11062:40208f18c7c2 11063:de20b0903d7d
    25 from logilab.common.deprecation import deprecated
    25 from logilab.common.deprecation import deprecated
    26 from logilab.common.graph import ordered_nodes
    26 from logilab.common.graph import ordered_nodes
    27 
    27 
    28 from rql.utils import rqlvar_maker
    28 from rql.utils import rqlvar_maker
    29 
    29 
    30 from cubicweb import Binary, ValidationError
    30 from cubicweb import Binary, ValidationError, neg_role
    31 from cubicweb.view import EntityAdapter
    31 from cubicweb.view import EntityAdapter
    32 from cubicweb.predicates import is_instance
    32 from cubicweb.predicates import is_instance
    33 from cubicweb.web import (INTERNAL_FIELD_VALUE, RequestError, NothingToEdit,
    33 from cubicweb.web import (INTERNAL_FIELD_VALUE, RequestError, NothingToEdit,
    34                           ProcessFormError)
    34                           ProcessFormError)
    35 from cubicweb.web.views import basecontrollers, autoform
    35 from cubicweb.web.views import basecontrollers, autoform
   184         # those two data variables are used to handle relation from/to entities
   184         # those two data variables are used to handle relation from/to entities
   185         # which doesn't exist at time where the entity is edited and that
   185         # which doesn't exist at time where the entity is edited and that
   186         # deserves special treatment
   186         # deserves special treatment
   187         req.data['pending_inlined'] = defaultdict(set)
   187         req.data['pending_inlined'] = defaultdict(set)
   188         req.data['pending_others'] = set()
   188         req.data['pending_others'] = set()
       
   189         req.data['pending_composite_delete'] = set()
   189         try:
   190         try:
   190             for formparams in self._ordered_formparams():
   191             for formparams in self._ordered_formparams():
   191                 eid = self.edit_entity(formparams)
   192                 eid = self.edit_entity(formparams)
   192         except (RequestError, NothingToEdit) as ex:
   193         except (RequestError, NothingToEdit) as ex:
   193             if '__linkto' in req.form and 'eid' in req.form:
   194             if '__linkto' in req.form and 'eid' in req.form:
   202         for form_, field in req.data.pop('pending_others'):
   203         for form_, field in req.data.pop('pending_others'):
   203             self.handle_formfield(form_, field)
   204             self.handle_formfield(form_, field)
   204         # then execute rql to set all relations
   205         # then execute rql to set all relations
   205         for querydef in self.relations_rql:
   206         for querydef in self.relations_rql:
   206             self._cw.execute(*querydef)
   207             self._cw.execute(*querydef)
       
   208         # delete pending composite
       
   209         for entity, rtype, role, targettype in req.data['pending_composite_delete']:
       
   210             # Check the composite relation was not re-set in the same form
       
   211             # (see test_reparent_subentity in web/test/unittest_application.py)
       
   212             entity.cw_clear_relation_cache(rtype, role)
       
   213             if not entity.related(rtype, role=role, targettypes=(targettype,)):
       
   214                 entity.cw_delete()
   207         # XXX this processes *all* pending operations of *all* entities
   215         # XXX this processes *all* pending operations of *all* entities
   208         if '__delete' in req.form:
   216         if '__delete' in req.form:
   209             todelete = req.list_form_param('__delete', req.form, pop=True)
   217             todelete = req.list_form_param('__delete', req.form, pop=True)
   210             if todelete:
   218             if todelete:
   211                 autoform.delete_relations(self._cw, todelete)
   219                 autoform.delete_relations(self._cw, todelete)
   301                     origvalues = set()
   309                     origvalues = set()
   302 
   310 
   303                 if value is None or value == origvalues:
   311                 if value is None or value == origvalues:
   304                     continue # not edited / not modified / to do later
   312                     continue # not edited / not modified / to do later
   305 
   313 
       
   314                 unlinked_eids = origvalues - value
       
   315                 if unlinked_eids:
       
   316                     # Special handling of composite relation removal
       
   317                     self.handle_composite_removal(
       
   318                         form, field, unlinked_eids)
       
   319 
   306                 if rschema.inlined and rqlquery is not None and field.role == 'subject':
   320                 if rschema.inlined and rqlquery is not None and field.role == 'subject':
   307                     self.handle_inlined_relation(form, field, value, origvalues, rqlquery)
   321                     self.handle_inlined_relation(form, field, value, origvalues, rqlquery)
   308                 elif form.edited_entity.has_eid():
   322                 elif form.edited_entity.has_eid():
   309                     self.handle_relation(form, field, value, origvalues)
   323                     self.handle_relation(form, field, value, origvalues)
   310                 else:
   324                 else:
   311                     form._cw.data['pending_others'].add( (form, field) )
   325                     form._cw.data['pending_others'].add( (form, field) )
   312 
   326 
   313         except ProcessFormError as exc:
   327         except ProcessFormError as exc:
   314             self.errors.append((field, exc))
   328             self.errors.append((field, exc))
       
   329 
       
   330     def handle_composite_removal(self, form, field, removed_values):
       
   331         """
       
   332         In EditController-handled forms, when the user removes a composite
       
   333         relation, it triggers the removal of the related entity in the
       
   334         composite. This is where this happens.
       
   335 
       
   336         See for instance test_subject_subentity_removal in
       
   337         web/test/unittest_application.py.
       
   338         """
       
   339         rschema = self._cw.vreg.schema.rschema(field.name)
       
   340         for unlinked_eid in removed_values:
       
   341             unlinked_entity = self._cw.entity_from_eid(unlinked_eid)
       
   342             rdef = rschema.role_rdef(form.edited_entity.cw_etype,
       
   343                                      unlinked_entity.cw_etype,
       
   344                                      field.role)
       
   345             if rdef.composite is not None:
       
   346                 if rdef.composite == field.role:
       
   347                     targettype = form.edited_entity.e_schema
       
   348                     to_be_removed = unlinked_entity
       
   349                 else:
       
   350                     targettype = unlinked_entity.e_schema
       
   351                     to_be_removed = form.edited_entity
       
   352                 self.info('Scheduling removal of %s as composite relation '
       
   353                           '%s was removed', to_be_removed, rdef)
       
   354                 form._cw.data['pending_composite_delete'].add(
       
   355                     (to_be_removed, field.name,
       
   356                      neg_role(rdef.composite), targettype))
   315 
   357 
   316     def handle_inlined_relation(self, form, field, values, origvalues, rqlquery):
   358     def handle_inlined_relation(self, form, field, values, origvalues, rqlquery):
   317         """handle edition for the (rschema, x) relation of the given entity
   359         """handle edition for the (rschema, x) relation of the given entity
   318         """
   360         """
   319         if values:
   361         if values: