web/views/editcontroller.py
changeset 11066 dcbb64d3a1d9
parent 11064 113e9da47afc
child 11114 468b91aabd9d
equal deleted inserted replaced
11065:c7dbd10648e6 11066:dcbb64d3a1d9
    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, neg_role
    30 from cubicweb import Binary, ValidationError
    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
   207             self.handle_formfield(form_, field)
   207             self.handle_formfield(form_, field)
   208         # then execute rql to set all relations
   208         # then execute rql to set all relations
   209         for querydef in self.relations_rql:
   209         for querydef in self.relations_rql:
   210             self._cw.execute(*querydef)
   210             self._cw.execute(*querydef)
   211         # delete pending composite
   211         # delete pending composite
   212         for entity, rtype, role, targettype in req.data['pending_composite_delete']:
   212         for entity in req.data['pending_composite_delete']:
   213             # Check the composite relation was not re-set in the same form
   213             entity.cw_delete()
   214             # (see test_reparent_subentity in web/test/unittest_application.py)
       
   215             entity.cw_clear_relation_cache(rtype, role)
       
   216             if not entity.related(rtype, role=role, targettypes=(targettype,)):
       
   217                 entity.cw_delete()
       
   218         # XXX this processes *all* pending operations of *all* entities
   214         # XXX this processes *all* pending operations of *all* entities
   219         if '__delete' in req.form:
   215         if '__delete' in req.form:
   220             todelete = req.list_form_param('__delete', req.form, pop=True)
   216             todelete = req.list_form_param('__delete', req.form, pop=True)
   221             if todelete:
   217             if todelete:
   222                 autoform.delete_relations(self._cw, todelete)
   218                 autoform.delete_relations(self._cw, todelete)
   311 
   307 
   312                 if entity.has_eid():
   308                 if entity.has_eid():
   313                     origvalues = set(data[0] for data in entity.related(field.name, field.role).rows)
   309                     origvalues = set(data[0] for data in entity.related(field.name, field.role).rows)
   314                 else:
   310                 else:
   315                     origvalues = set()
   311                     origvalues = set()
   316 
       
   317                 if value is None or value == origvalues:
   312                 if value is None or value == origvalues:
   318                     continue # not edited / not modified / to do later
   313                     continue # not edited / not modified / to do later
   319 
   314 
   320                 unlinked_eids = origvalues - value
   315                 unlinked_eids = origvalues - value
       
   316 
   321                 if unlinked_eids:
   317                 if unlinked_eids:
   322                     # Special handling of composite relation removal
   318                     # Special handling of composite relation removal
   323                     self.handle_composite_removal(
   319                     self.handle_composite_removal(
   324                         form, field, unlinked_eids, rqlquery)
   320                         form, field, unlinked_eids, value, rqlquery)
   325 
   321 
   326                 if rschema.inlined and rqlquery is not None and field.role == 'subject':
   322                 if rschema.inlined and rqlquery is not None and field.role == 'subject':
   327                     self.handle_inlined_relation(form, field, value, origvalues, rqlquery)
   323                     self.handle_inlined_relation(form, field, value, origvalues, rqlquery)
   328                 elif form.edited_entity.has_eid():
   324                 elif form.edited_entity.has_eid():
   329                     self.handle_relation(form, field, value, origvalues)
   325                     self.handle_relation(form, field, value, origvalues)
   331                     form._cw.data['pending_others'].add( (form, field) )
   327                     form._cw.data['pending_others'].add( (form, field) )
   332 
   328 
   333         except ProcessFormError as exc:
   329         except ProcessFormError as exc:
   334             self.errors.append((field, exc))
   330             self.errors.append((field, exc))
   335 
   331 
   336     def handle_composite_removal(self, form, field, removed_values, rqlquery):
   332     def handle_composite_removal(self, form, field,
       
   333                                  removed_values, new_values, rqlquery):
   337         """
   334         """
   338         In EditController-handled forms, when the user removes a composite
   335         In EditController-handled forms, when the user removes a composite
   339         relation, it triggers the removal of the related entity in the
   336         relation, it triggers the removal of the related entity in the
   340         composite. This is where this happens.
   337         composite. This is where this happens.
   341 
   338 
   342         See for instance test_subject_subentity_removal in
   339         See for instance test_subject_subentity_removal in
   343         web/test/unittest_application.py.
   340         web/test/unittest_application.py.
   344         """
   341         """
   345         rschema = self._cw.vreg.schema.rschema(field.name)
   342         rschema = self._cw.vreg.schema.rschema(field.name)
       
   343         new_value_etypes = set(self._cw.entity_from_eid(eid).cw_etype
       
   344                                for eid in new_values)
   346         for unlinked_eid in removed_values:
   345         for unlinked_eid in removed_values:
   347             unlinked_entity = self._cw.entity_from_eid(unlinked_eid)
   346             unlinked_entity = self._cw.entity_from_eid(unlinked_eid)
   348             rdef = rschema.role_rdef(form.edited_entity.cw_etype,
   347             rdef = rschema.role_rdef(form.edited_entity.cw_etype,
   349                                      unlinked_entity.cw_etype,
   348                                      unlinked_entity.cw_etype,
   350                                      field.role)
   349                                      field.role)
   351             if rdef.composite is not None:
   350             if rdef.composite is not None:
   352                 if rdef.composite == field.role:
   351                 if rdef.composite == field.role:
   353                     targettype = form.edited_entity.e_schema
       
   354                     to_be_removed = unlinked_entity
   352                     to_be_removed = unlinked_entity
   355                 else:
   353                 else:
   356                     targettype = unlinked_entity.e_schema
   354                     if unlinked_entity.cw_etype in new_value_etypes:
       
   355                         # This is a same-rdef re-parenting: do not remove the entity
       
   356                         continue
   357                     to_be_removed = form.edited_entity
   357                     to_be_removed = form.edited_entity
   358                     self.info('Edition of %s is cancelled (deletion requested)',
   358                     self.info('Edition of %s is cancelled (deletion requested)',
   359                               to_be_removed)
   359                               to_be_removed)
   360                     rqlquery.canceled = True
   360                     rqlquery.canceled = True
   361                 self.info('Scheduling removal of %s as composite relation '
   361                 self.info('Scheduling removal of %s as composite relation '
   362                           '%s was removed', to_be_removed, rdef)
   362                           '%s was removed', to_be_removed, rdef)
   363                 form._cw.data['pending_composite_delete'].add(
   363                 form._cw.data['pending_composite_delete'].add(to_be_removed)
   364                     (to_be_removed, field.name,
       
   365                      neg_role(rdef.composite), targettype))
       
   366 
   364 
   367     def handle_inlined_relation(self, form, field, values, origvalues, rqlquery):
   365     def handle_inlined_relation(self, form, field, values, origvalues, rqlquery):
   368         """handle edition for the (rschema, x) relation of the given entity
   366         """handle edition for the (rschema, x) relation of the given entity
   369         """
   367         """
   370         if values:
   368         if values: