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