# HG changeset patch # User sylvain.thenault@logilab.fr # Date 1239959659 -7200 # Node ID d6279efff7b31bcec7951ef4c1bdf7e28a23bc91 # Parent 2a80b14fc548d4c05bc64de70e5b8396f830c474 refactor the way field value/display value are handled to avoid getting a 'display' value when expected a 'typed' value diff -r 2a80b14fc548 -r d6279efff7b3 web/form.py --- a/web/form.py Fri Apr 17 11:13:44 2009 +0200 +++ b/web/form.py Fri Apr 17 11:14:19 2009 +0200 @@ -337,49 +337,64 @@ form_render() """ self.context = context = {} - # on validation error, we get a dictionnary of previously submitted values - if values is None: - values = {} - previous_values = self.req.data.get('formvalues') - if previous_values: - values.update(previous_values) + # on validation error, we get a dictionary of previously submitted + # values + self._previous_values = self.req.data.get('formvalues', {}) + # ensure rendervalues is a dict + if rendervalues is None: + rendervalues = {} for field in self.fields: for field in field.actual_fields(self): field.form_init(self) - value = self.form_field_value(field, values) - context[field] = {'value': field.format_value(self.req, value), - 'rawvalue': value, + value = self.form_field_display_value(field, rendervalues) + context[field] = {'value': value, 'name': self.form_field_name(field), 'id': self.form_field_id(field), } - def form_field_value(self, field, values, load_bytes=False): - """looks for field's value in - 1. kw args given to render_form (including previously submitted form - values if any) + def form_field_display_value(self, field, rendervalues, load_bytes=False): + """return field's *string* value to use for display + + looks in + 1. previously submitted form values if any (eg on validation error) 2. req.form - 3. default_ attribute / method on the form - 4. field's initial value + 3. extra kw args given to render_form + 4. field's typed value + + values found in 1. and 2. are expected te be already some 'display' + value while those found in 3. and 4. are expected to be correctly typed. """ - defaultattr = 'default_%s' % field.name - if field.name in values: - value = values[field.name] + if field.name in self._previous_values: + value = self._previous_values[field.name] elif field.name in self.req.form: value = self.req.form[field.name] - elif hasattr(self, defaultattr): - value = getattr(self, defaultattr) - if callable(value): - value = value() else: - value = field.initial - if callable(value): - value = value(self) + if field.name in rendervalues: + value = rendervalues[field.name] + else: + value = self.form_field_value(field, load_bytes) + if callable(value): + value = value(self) + if value != INTERNAL_FIELD_VALUE: + value = field.format_value(self.req, value) return value + + def form_field_value(self, field, load_bytes=False): + """return field's *typed* value""" + value = field.initial + if callable(value): + value = value(self) + return value + + def _errex_match_field(self, errex, field): + """return true if the field has some error in given validation exception + """ + return field.name in errex.errors def form_field_error(self, field): """return validation error for widget's field, if any""" errex = self.req.data.get('formerrors') - if errex and field.name in errex.errors: + if errex and self._errex_match_field(errex, field): self.req.data['displayederrors'].add(field.name) return u'%s' % errex.errors[field.name] return u'' @@ -447,64 +462,68 @@ name = 'edito-%s' % field.name return HiddenInitialValueField(field, name=name) - def form_field_value(self, field, values, load_bytes=False): - """look for field's value with the following rules: - 1. handle special __type and eid fields - 2. looks in kw args given to render_form (including previously submitted - form values if any) - 3. looks in req.form - 4. if entity has an eid: - 1. looks for an associated attribute / method - 2. use field's initial value - else: - 1. looks for a default_ attribute / method on the form - 2. use field's initial value - - values found in step 4 may be a callable which'll then be called. + def form_field_value(self, field, load_bytes=False): + """return field's *typed* value + + overriden to deal with + * special eid / __type / edits- / edito- fields + * lookup for values on edited entities """ - fieldname = field.name - if fieldname.startswith('edits-') or fieldname.startswith('edito-'): + attr = field.name + entity = self.edited_entity + if attr == 'eid': + return entity.eid + if not field.eidparam: + return super(EntityFieldsForm, self).form_field_value(field, load_bytes) + if attr.startswith('edits-') or attr.startswith('edito-'): # edit[s|o]- fieds must have the actual value stored on the entity - if hasattr(field, 'visible_field'): - if self.edited_entity.has_eid(): - value = self._form_field_entity_value(field.visible_field, - default_initial=False) - else: - value = INTERNAL_FIELD_VALUE + assert hasattr(field, 'visible_field') + vfield = field.visible_field + assert vfield.eidparam + if entity.has_eid(): + return self.form_field_value(vfield) + return INTERNAL_FIELD_VALUE + if attr == '__type': + return entity.id + if field.role == 'object': + attr = 'reverse_' + attr + elif entity.e_schema.subject_relation(attr).is_final(): + attrtype = entity.e_schema.destination(attr) + if attrtype == 'Password': + return entity.has_eid() and INTERNAL_FIELD_VALUE or '' + if attrtype == 'Bytes': + if entity.has_eid(): + if load_bytes: + return getattr(entity, attr) + # XXX value should reflect if some file is already attached + return True + return False + if entity.has_eid(): + value = getattr(entity, attr) else: - value = field.initial - elif fieldname == '__type': - value = self.edited_entity.id - elif fieldname == 'eid': - value = self.edited_entity.eid - elif fieldname in values: - value = values[fieldname] - elif fieldname in self.req.form: - value = self.req.form[fieldname] + value = self._form_field_default_value(field, load_bytes) + return value + # non final relation field + if entity.has_eid(): + value = [ent.eid for ent in getattr(entity, attr)] else: - if self.edited_entity.has_eid() and field.eidparam: - # use value found on the entity or field's initial value if it's - # not an attribute of the entity (XXX may conflicts and get - # undesired value) - value = self._form_field_entity_value(field, default_initial=True, - load_bytes=load_bytes) - else: - defaultattr = 'default_%s' % fieldname - if hasattr(self.edited_entity, defaultattr): - # XXX bw compat, default_ on the entity - warn('found %s on %s, should be set on a specific form' - % (defaultattr, self.edited_entity.id), DeprecationWarning) - value = getattr(self.edited_entity, defaultattr) - elif hasattr(self, defaultattr): - # search for default_ on the form instance - value = getattr(self, defaultattr) - else: - # use field's initial value - value = field.initial + value = self._form_field_default_value(field, load_bytes) + return value + + def _form_field_default_value(self, field, load_bytes): + defaultattr = 'default_%s' % field.name + if hasattr(self.edited_entity, defaultattr): + # XXX bw compat, default_ on the entity + warn('found %s on %s, should be set on a specific form' + % (defaultattr, self.edited_entity.id), DeprecationWarning) + value = getattr(self.edited_entity, defaultattr) if callable(value): - value = value(self) + value = value() + else: + value = super(EntityFieldsForm, self).form_field_value(field, + load_bytes) return value - + def form_field_format(self, field): entity = self.edited_entity if field.eidparam and entity.e_schema.has_metadata(field.name, 'format') and ( @@ -519,38 +538,11 @@ return self.edited_entity.attr_metadata(field.name, 'encoding') return super(EntityFieldsForm, self).form_field_encoding(field) - def form_field_error(self, field): - """return validation error for widget's field, if any""" - errex = self.req.data.get('formerrors') - if errex and errex.eid == self.edited_entity.eid and field.name in errex.errors: - self.req.data['displayederrors'].add(field.name) - return u'%s' % errex.errors[field.name] - return u'' - - def _form_field_entity_value(self, field, default_initial=True, load_bytes=False): - attr = field.name - entity = self.edited_entity - if field.role == 'object': - attr = 'reverse_' + attr - elif entity.e_schema.subject_relation(attr).is_final(): - attrtype = entity.e_schema.destination(attr) - if attrtype == 'Password': - return entity.has_eid() and INTERNAL_FIELD_VALUE or '' - if attrtype == 'Bytes': - if entity.has_eid(): - if load_bytes: - return getattr(entity, attr) - # XXX value should reflect if some file is already attached - return True - return False - if default_initial: - value = getattr(entity, attr, field.initial) - else: - value = getattr(entity, attr) - if isinstance(field, RelationField): - # in this case, value is the list of related entities - value = [ent.eid for ent in value] - return value + + def _errex_match_field(self, errex, field): + """return true if the field has some error in given validation exception + """ + return errex.eid == self.edited_entity.eid and field.name in errex.errors def form_field_name(self, field): if field.eidparam: diff -r 2a80b14fc548 -r d6279efff7b3 web/formfields.py --- a/web/formfields.py Fri Apr 17 11:13:44 2009 +0200 +++ b/web/formfields.py Fri Apr 17 11:14:19 2009 +0200 @@ -303,7 +303,7 @@ def render(self, form, renderer): wdgs = [super(EditableFileField, self).render(form, renderer)] if form.form_field_format(self) in self.editable_formats: - data = form.form_field_value(self, {}, load_bytes=True) + data = form.form_field_value(self, load_bytes=True) if data: encoding = form.form_field_encoding(self) try: diff -r 2a80b14fc548 -r d6279efff7b3 web/formwidgets.py --- a/web/formwidgets.py Fri Apr 17 11:13:44 2009 +0200 +++ b/web/formwidgets.py Fri Apr 17 11:14:19 2009 +0200 @@ -251,19 +251,19 @@ return txtwidget + cal_button def _render_calendar_popup(self, form, field): - req = form.req - value = form.context[field]['rawvalue'] + value = form.form_field_value(field) + if not value: + value = date.today() inputid = form.context[field]['id'] helperid = '%shelper' % inputid - if not value: - value = date.today() year, month = value.year, value.month return (u""" """ % (helperid, inputid, year, month, - req.external_resource('CALENDAR_ICON'), - req._('calendar'), helperid) ) + form.req.external_resource('CALENDAR_ICON'), + form.req._('calendar'), helperid) ) + # ajax widgets ################################################################