# HG changeset patch # User Sylvain Thénault # Date 1261423681 -3600 # Node ID f1b9f0ed1253fbfb1965203dcb857b73b59a93aa # Parent c325c62cec8e726dcc48389d19ecc26df9b79533 make new editcontroller works, based on a _cw_edited_fields hidden input diff -r c325c62cec8e -r f1b9f0ed1253 web/formfields.py --- a/web/formfields.py Mon Dec 21 20:27:14 2009 +0100 +++ b/web/formfields.py Mon Dec 21 20:28:01 2009 +0100 @@ -790,12 +790,22 @@ def process_form_value(self, form): """process posted form and return correctly typed value""" widget = self.get_widget(form) - value = widget.process_field_data(form, self) - if value is None: - return () - elif not isinstance(value, list): - return (value,) - return value + values = widget.process_field_data(form, self) + if values is None: + values = () + elif not isinstance(values, list): + values = (values,) + eids = set() + for eid in values: + if not eid: # AutoCompletionWidget XXX deal with this in the widget + continue + typed_eid = form.actual_eid(eid) + if typed_eid is None: + form._cw.data['pendingfields'].append( (form, self) ) + return None + eids.add(typed_eid) + return eids + class CompoundField(Field): def __init__(self, fields, *args, **kwargs): diff -r c325c62cec8e -r f1b9f0ed1253 web/views/editcontroller.py --- a/web/views/editcontroller.py Mon Dec 21 20:27:14 2009 +0100 +++ b/web/views/editcontroller.py Mon Dec 21 20:28:01 2009 +0100 @@ -72,7 +72,7 @@ req.set_shared_data('__maineid', form['__maineid'], querydata=True) # no specific action, generic edition self._to_create = req.data['eidmap'] = {} - self._pending_relations = [] + self._pending_fields = req.data['pendingfields'] = [] todelete = self._cw.get_pending_deletes() toinsert = self._cw.get_pending_inserts() try: @@ -85,19 +85,18 @@ method = getattr(entity, methodname) method(formparams) eid = self.edit_entity(formparams) - except (RequestError, NothingToEdit): + except (RequestError, NothingToEdit), ex: if '__linkto' in req.form and 'eid' in req.form: self.execute_linkto() elif not ('__delete' in req.form or '__insert' in req.form or todelete or toinsert): - raise ValidationError(None, {None: req._('nothing to edit')}) + raise ValidationError(None, {None: unicode(ex)}) + # handle relations in newly created entities + if self._pending_fields: + for form, field in self._pending_fields: + self.handle_formfield(form, field) + # execute rql to set all relations for querydef in self.relations_rql: self._cw.execute(*querydef) - # handle relations in newly created entities - # XXX find a way to merge _pending_relations and relations_rql - if self._pending_relations: - for form, field, entity in self._pending_relations: - for querydef in self.handle_relation(form, field, entity, True): - self._cw.execute(*querydef) # XXX this processes *all* pending operations of *all* entities if req.form.has_key('__delete'): todelete += req.list_form_param('__delete', req.form, pop=True) @@ -108,6 +107,9 @@ if toinsert: self.insert_relations(parse_relations_descr(toinsert)) self._cw.remove_pending_operations() + if self.errors: + errors = dict((f.name, unicode(ex)) for f, ex in self.errors) + raise ValidationError(form.get('__maineid'), errors) def _insert_entity(self, etype, eid, rqlquery): rql = rqlquery.insert_query(etype) @@ -125,17 +127,15 @@ return neweid def _update_entity(self, eid, rqlquery): - rql = rqlquery.update_query(eid) - self._cw.execute(rql, rqlquery.kwargs) + self._cw.execute(rqlquery.update_query(eid), rqlquery.kwargs) def edit_entity(self, formparams, multiple=False): """edit / create / copy an entity and return its eid""" etype = formparams['__type'] entity = self._cw.vreg['etypes'].etype_class(etype)(self._cw) entity.eid = formparams['eid'] - eid = self._get_eid(entity.eid) is_main_entity = self._cw.form.get('__maineid') == formparams['eid'] - # let a chance to do some entity specific stuff.tn + # let a chance to do some entity specific stuff entity.pre_web_edit() # create a rql query from parameters rqlquery = RqlQuery() @@ -144,9 +144,26 @@ # a few dark corners formid = self._cw.form.get('__form_id', 'edition') form = self._cw.vreg['forms'].select(formid, self._cw, entity=entity) - for field in form.fields: - if form.form_field_modified(field): - self.handle_formfield(form, field, entity, rqlquery) + eid = form.actual_eid(entity.eid) + try: + editedfields = formparams['_cw_edited_fields'] + except KeyError: + raise RequestError(self._cw._('no edited fields specified for entity %s' % entity.eid)) + for editedfield in splitstrip(editedfields): + try: + name, role = editedfield.split('-') + except: + name = editedfield + role = None + if form.field_by_name.im_func.func_code.co_argcount == 4: + field = form.field_by_name(name, role, eschema=entity.e_schema) + else: + field = form.field_by_name(name, role) + if field.has_been_modified(form): + self.handle_formfield(form, field, rqlquery) + if self.errors: + errors = dict((f.name, unicode(ex)) for f, ex in self.errors) + raise ValidationError(entity.eid, errors) if eid is None: # creation or copy entity.eid = self._insert_entity(etype, formparams['eid'], rqlquery) elif rqlquery.edited: # edition of an existant entity @@ -165,10 +182,10 @@ self.execute_linkto(entity.eid) return eid - def handle_formfield(self, form, field, entity, rqlquery): - eschema = entity.e_schema + def handle_formfield(self, form, field, rqlquery=None): + eschema = form.edited_entity.e_schema try: - for attr, value in field.process_posted(form): + for field, value in field.process_posted(form): if not ( (field.role == 'subject' and field.name in eschema.subjrels) or @@ -176,16 +193,64 @@ continue rschema = self._cw.vreg.schema.rschema(field.name) if rschema.final: - rqlquery.kwargs[attr] = value - rqlquery.edited.append('X %s %%(%s)s' % (attr, attr)) - elif rschema.inlined: - self.handle_inlined_relation(form, field, entity, rqlquery) + rqlquery.kwargs[field.name] = value + rqlquery.edited.append('X %s %%(%s)s' % (rschema, rschema)) else: - self.relations_rql += self.handle_relation( - form, field, entity) + if form.edited_entity.has_eid(): + origvalues = set(entity.eid for entity in form.edited_entity.related(field.name, field.role, entities=True)) + else: + origvalues = set() + if value is None or value == origvalues: + continue # not edited / not modified / to do later + if rschema.inlined and rqlquery is not None: + self.handle_inlined_relation(form, field, value, origvalues, rqlquery) + elif form.edited_entity.has_eid(): + self.handle_relation(form, field, value, origvalues) + else: + self._pending_fields.append( (form, field) ) + except ProcessFormError, exc: self.errors.append((field, exc)) + def handle_inlined_relation(self, form, field, values, origvalues, rqlquery): + """handle edition for the (rschema, x) relation of the given entity + """ + attr = field.name + if values: + rqlquery.kwargs[attr] = iter(values).next() + rqlquery.edited.append('X %s %s' % (attr, attr.upper())) + rqlquery.restrictions.append('%s eid %%(%s)s' % (attr.upper(), attr)) + elif form.edited_entity.has_eid(): + self.handle_relation(form, field, values, origvalues) + + def handle_relation(self, form, field, values, origvalues): + """handle edition for the (rschema, x) relation of the given entity + """ + etype = form.edited_entity.e_schema + rschema = self._cw.vreg.schema.rschema(field.name) + if field.role == 'subject': + desttype = rschema.objects(etype)[0] + card = rschema.rdef(etype, desttype).cardinality[0] + subjvar, objvar = 'X', 'Y' + else: + desttype = rschema.subjects(etype)[0] + card = rschema.rdef(desttype, etype).cardinality[1] + subjvar, objvar = 'Y', 'X' + eid = form.edited_entity.eid + if field.role == 'object' or not rschema.inlined or not values: + # this is not an inlined relation or no values specified, + # explicty remove relations + rql = 'DELETE %s %s %s WHERE X eid %%(x)s, Y eid %%(y)s' % ( + subjvar, rschema, objvar) + for reid in origvalues.difference(values): + self.relations_rql.append((rql, {'x': eid, 'y': reid}, ('x', 'y'))) + seteids = values.difference(origvalues) + if seteids: + rql = 'SET %s %s %s WHERE X eid %%(x)s, Y eid %%(y)s' % ( + subjvar, rschema, objvar) + for reid in seteids: + self.relations_rql.append((rql, {'x': eid, 'y': reid}, ('x', 'y'))) + def _action_apply(self): self._default_publish() self.reset() @@ -201,92 +266,4 @@ self.delete_entities(self._cw.edited_eids(withtype=True)) return self.reset() - def _relation_values(self, form, field, entity, late=False): - """handle edition for the (rschema, x) relation of the given entity - """ - values = set() - for eid in field.process_form_value(form): - if not eid: # AutoCompletionWidget - continue - typed_eid = self._get_eid(eid) - if typed_eid is None: - if late: - # eid is still None while it's already a late call - # this mean that the associated entity has not been created - raise Exception("eid %s is still not created" % eid) - self._pending_relations.append( (form, field, entity) ) - return None - values.add(typed_eid) - return values - def handle_inlined_relation(self, form, field, entity, rqlquery): - """handle edition for the (rschema, x) relation of the given entity - """ - if entity.has_eid(): - origvalues = set(row[0] for row in entity.related(field.name, field.role)) - else: - origvalues = set() - values = self._relation_values(form, field, entity) - if values is None or values == origvalues: - return # not edited / not modified / to do later - attr = field.name - if values: - rqlquery.kwargs[attr] = iter(values).next() - rqlquery.edited.append('X %s %s' % (attr, attr.upper())) - rqlquery.restrictions.append('%s eid %%(%s)s' % (attr.upper(), attr)) - elif entity.has_eid(): - self.relations_rql += self.handle_relation(form, field, entity) - - def handle_relation(self, form, field, entity, late=False): - """handle edition for the (rschema, x) relation of the given entity - """ - if entity.has_eid(): - origvalues = set(row[0] for row in entity.related(field.name, field.role)) - else: - origvalues = set() - values = self._relation_values(form, field, entity, late) - if values is None or values == origvalues: - return # not edited / not modified / to do later - etype = entity.e_schema - rschema = self._cw.vreg.schema.rschema(field.name) - if field.role == 'subject': - desttype = rschema.objects(etype)[0] - card = rschema.rproperty(etype, desttype, 'cardinality')[0] - subjvar, objvar = 'X', 'Y' - else: - desttype = rschema.subjects(etype)[0] - card = rschema.rproperty(desttype, etype, 'cardinality')[1] - subjvar, objvar = 'Y', 'X' - eid = entity.eid - if field.role == 'object' or not rschema.inlined or not values: - # this is not an inlined relation or no values specified, - # explicty remove relations - rql = 'DELETE %s %s %s WHERE X eid %%(x)s, Y eid %%(y)s' % ( - subjvar, rschema, objvar) - for reid in origvalues.difference(values): - yield (rql, {'x': eid, 'y': reid}, ('x', 'y')) - seteids = values.difference(origvalues) - if seteids: - rql = 'SET %s %s %s WHERE X eid %%(x)s, Y eid %%(y)s' % ( - subjvar, rschema, objvar) - for reid in seteids: - yield (rql, {'x': eid, 'y': reid}, ('x', 'y')) - - def _get_eid(self, eid): - # should be either an int (existant entity) or a variable (to be - # created entity) - assert eid or eid == 0, repr(eid) # 0 is a valid eid - try: - return typed_eid(eid) - except ValueError: - try: - return self._to_create[eid] - except KeyError: - self._to_create[eid] = None - return None - - def _linked_eids(self, eids, late=False): - """return a list of eids if they are all known, else raise ToDoLater - """ - -