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: |