# HG changeset patch # User Sylvain Thénault # Date 1271861605 -7200 # Node ID 4176a50c81c9a186af9c359570e1a25da5f794c0 # Parent 5f116a4d8a5492f0555d96af11a69583f416be1e [form] small api cleanup and refactoring before documenting the form system diff -r 5f116a4d8a54 -r 4176a50c81c9 web/form.py --- a/web/form.py Wed Apr 21 16:48:45 2010 +0200 +++ b/web/form.py Wed Apr 21 16:53:25 2010 +0200 @@ -162,16 +162,34 @@ cls_or_self._fieldsattr().append(field) @iclassmethod - def insert_field_before(cls_or_self, new_field, name, role='subject'): - field = cls_or_self.field_by_name(name, role) + def insert_field_before(cls_or_self, field, name, role=None): + """Insert the given field before the field of given name and role.""" + bfield = cls_or_self.field_by_name(name, role) fields = cls_or_self._fieldsattr() - fields.insert(fields.index(field), new_field) + fields.insert(fields.index(bfield), field) + + @iclassmethod + def insert_field_after(cls_or_self, field, name, role=None): + """Insert the given field after the field of given name and role.""" + afield = cls_or_self.field_by_name(name, role) + fields = cls_or_self._fieldsattr() + fields.insert(fields.index(afield)+1, field) @iclassmethod - def insert_field_after(cls_or_self, new_field, name, role='subject'): - field = cls_or_self.field_by_name(name, role) - fields = cls_or_self._fieldsattr() - fields.insert(fields.index(field)+1, new_field) + def add_hidden(cls_or_self, name, value=None, **kwargs): + """Append an hidden field to the form. `name`, `value` and extra keyword + arguments will be given to the field constructor. The inserted field is + returned. + """ + kwargs.setdefault('ignore_req_params', True) + kwargs.setdefault('widget', fwdgs.HiddenInput) + field = formfields.StringField(name=name, value=value, **kwargs) + if 'id' in kwargs: + # by default, hidden input don't set id attribute. If one is + # explicitly specified, ensure it will be set + field.widget.setdomid = True + cls_or_self.append_field(field) + return field def session_key(self): """return the key that may be used to store / retreive data about a diff -r 5f116a4d8a54 -r 4176a50c81c9 web/formfields.py --- a/web/formfields.py Wed Apr 21 16:48:45 2010 +0200 +++ b/web/formfields.py Wed Apr 21 16:53:25 2010 +0200 @@ -13,6 +13,7 @@ from datetime import datetime from logilab.mtconverter import xml_escape +from logilab.common import nullobject from logilab.common.date import ustrftime from yams.schema import KNOWN_METAATTRIBUTES, role_name @@ -45,7 +46,7 @@ result += sorted(partresult) return result -_MARKER = object() +_MARKER = nullobject() class Field(object): """This class is the abstract base class for all fields. It hold a bunch @@ -601,6 +602,7 @@ return value +# XXX turn into a widget class EditableFileField(FileField): editable_formats = ('text/plain', 'text/html', 'text/rest') diff -r 5f116a4d8a54 -r 4176a50c81c9 web/formwidgets.py --- a/web/formwidgets.py Wed Apr 21 16:48:45 2010 +0200 +++ b/web/formwidgets.py Wed Apr 21 16:53:25 2010 +0200 @@ -62,24 +62,17 @@ def format_value(self, form, field, value): return field.format_value(form._cw, value) - def values_and_attributes(self, form, field): - """found field's *string* value in: - 1. previously submitted form values if any (eg on validation error) - 2. req.form - 3. extra form values given to render() - 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. - - 3 and 4 are handle by the .typed_value(form, field) method + def attributes(self, form, field): + """Return HTML attributes for the widget, automatically setting DOM + identifier and tabindex when desired (see :attr:`setdomid` and + :attr:`settabindex` attributes) """ attrs = dict(self.attrs) if self.setdomid: attrs['id'] = field.dom_id(form, self.suffix) if self.settabindex and not 'tabindex' in attrs: attrs['tabindex'] = form._cw.next_tabindex() - return self.values(form, field), attrs + return attrs def values(self, form, field): values = None @@ -125,6 +118,10 @@ val = val.strip() return val + # XXX deprecates + def values_and_attributes(self, form, field): + return self.values(form, field), self.attributes(form, field) + @deprecated('[3.6] use values_and_attributes') def _render_attrs(self, form, field): """return html tag name, attributes and a list of values for the field @@ -204,10 +201,9 @@ """""" type = 'file' - def values_and_attributes(self, form, field): + def values(self, form, field): # ignore value which makes no sense here (XXX even on form validation error?) - values, attrs = super(FileInput, self).values_and_attributes(form, field) - return ('',), attrs + return ('',) class HiddenInput(Input): @@ -267,7 +263,7 @@ super(Select, self).__init__(attrs, **kwargs) self._multiple = multiple - def render(self, form, field, renderer): + def _render(self, form, field, renderer): curvalues, attrs = self.values_and_attributes(form, field) if not 'size' in attrs: attrs['size'] = self._multiple and '5' or '1' @@ -304,10 +300,20 @@ type = 'checkbox' vocabulary_widget = True - def render(self, form, field, renderer): + def __init__(self, attrs=None, separator=u'
\n', **kwargs): + super(CheckBox, self).__init__(attrs, **kwargs) + self.separator = separator + + def _render(self, form, field, renderer): curvalues, attrs = self.values_and_attributes(form, field) domid = attrs.pop('id', None) - sep = attrs.pop('separator', u'
\n') + # XXX turn this as initializer argument + try: + sep = attrs.pop('separator') + warn('[3.8] separator should be specified using initializer argument', + DeprecationWarning) + except KeyError: + sep = self.separator options = [] for i, option in enumerate(field.vocabulary(form)): try: @@ -332,52 +338,7 @@ input will be generated for each possible value. """ type = 'radio' - - -# compound widgets ############################################################# - -class IntervalWidget(FieldWidget): - """custom widget to display an interval composed by 2 fields. This widget - is expected to be used with a CompoundField containing the two actual - fields. - - Exemple usage:: - -from uicfg import autoform_field, autoform_section -autoform_field.tag_attribute(('Concert', 'minprice'), - CompoundField(fields=(IntField(name='minprice'), - IntField(name='maxprice')), - label=_('price'), - widget=IntervalWidget() - )) -# we've to hide the other field manually for now -autoform_section.tag_attribute(('Concert', 'maxprice'), 'generated') - """ - def render(self, form, field, renderer): - actual_fields = field.fields - assert len(actual_fields) == 2 - return u'
%s %s %s %s
' % ( - form._cw._('from_interval_start'), - actual_fields[0].render(form, renderer), - form._cw._('to_interval_end'), - actual_fields[1].render(form, renderer), - ) - - -class HorizontalLayoutWidget(FieldWidget): - """custom widget to display a set of fields grouped together horizontally - in a form. See `IntervalWidget` for example usage. - """ - def render(self, form, field, renderer): - if self.attrs.get('display_label', True): - subst = self.attrs.get('label_input_substitution', '%(label)s %(input)s') - fields = [subst % {'label': renderer.render_label(form, f), - 'input': f.render(form, renderer)} - for f in field.subfields(form)] - else: - fields = [f.render(form, renderer) for f in field.subfields(form)] - return u'
%s
' % ' '.join(fields) - + # javascript widgets ########################################################### @@ -404,7 +365,7 @@ req.html_headers.define_var('MONTHNAMES', monthnames) req.html_headers.define_var('DAYNAMES', daynames) - def render(self, form, field, renderer): + def _render(self, form, field, renderer): txtwidget = super(DateTimePicker, self).render(form, field, renderer) self.add_localized_infos(form._cw) cal_button = self._render_calendar_popup(form, field) @@ -560,21 +521,25 @@ try: self.autocomplete_initfunc = kwargs.pop('autocomplete_initfunc') except KeyError: - warn('use autocomplete_initfunc argument of %s constructor ' + warn('[3.6] use autocomplete_initfunc argument of %s constructor ' 'instead of relying on autocomplete_initfuncs dictionary on ' 'the entity class' % self.__class__.__name__, DeprecationWarning) self.autocomplete_initfunc = None super(AutoCompletionWidget, self).__init__(*args, **kwargs) - def values_and_attributes(self, form, field): - values, attrs = super(AutoCompletionWidget, self).values_and_attributes(form, field) + def values(self, form, field): + values = super(AutoCompletionWidget, self).values(form, field) + if not values: + values = ('',) + return values + + def attributes(self, form, field): + attrs = super(AutoCompletionWidget, self).attributes(form, field) init_ajax_attributes(attrs, self.wdgtype, self.loadtype) # XXX entity form specific attrs['cubicweb:dataurl'] = self._get_url(form.edited_entity, field) - if not values: - values = ('',) - return values, attrs + return attrs def _get_url(self, entity, field): if self.autocomplete_initfunc is None: @@ -609,8 +574,6 @@ wdgtype = 'LazySuggestField' def values_and_attributes(self, form, field): - self.add_media(form) - """override values_and_attributes to handle initial displayed values""" values, attrs = super(LazyRestrictedAutoCompletionWidget, self).values_and_attributes(form, field) assert len(values) == 1, "multiple selection is not supported yet by LazyWidget" @@ -628,22 +591,22 @@ return values, attrs def display_value_for(self, form, value): - entity =form._cw.entity_from_eid(value) + entity = form._cw.entity_from_eid(value) return entity.view('combobox') class AddComboBoxWidget(Select): - def values_and_attributes(self, form, field): - values, attrs = super(AddComboBoxWidget, self).values_and_attributes(form, field) - init_ajax_attributes(self.attrs, 'AddComboBox') + def attributes(self, form, field): + attrs = super(AddComboBoxWidget, self).attributes(form, field) + init_ajax_attributes(attrs, 'AddComboBox') # XXX entity form specific entity = form.edited_entity attrs['cubicweb:etype_to'] = entity.e_schema etype_from = entity.e_schema.subjrels[field.name].objects(entity.e_schema)[0] attrs['cubicweb:etype_from'] = etype_from - return values, attrs + return attrs - def render(self, form, field, renderer): + def _render(self, form, field, renderer): return super(AddComboBoxWidget, self).render(form, field, renderer) + u'''
diff -r 5f116a4d8a54 -r 4176a50c81c9 web/views/forms.py --- a/web/views/forms.py Wed Apr 21 16:48:45 2010 +0200 +++ b/web/views/forms.py Wed Apr 21 16:53:25 2010 +0200 @@ -16,7 +16,7 @@ from cubicweb import typed_eid from cubicweb.selectors import non_final_entity, match_kwargs, one_line_rset from cubicweb.web import uicfg, form, formwidgets as fwdgs -from cubicweb.web.formfields import StringField, relvoc_unrelated, guess_field +from cubicweb.web.formfields import relvoc_unrelated, guess_field class FieldsForm(form.Form): @@ -75,18 +75,6 @@ """true if the form needs enctype=multipart/form-data""" return any(field.needs_multipart for field in self.fields) - def add_hidden(self, name, value=None, **kwargs): - """add an hidden field to the form""" - kwargs.setdefault('ignore_req_params', True) - kwargs.setdefault('widget', fwdgs.HiddenInput) - field = StringField(name=name, value=value, **kwargs) - if 'id' in kwargs: - # by default, hidden input don't set id attribute. If one is - # explicitly specified, ensure it will be set - field.widget.setdomid = True - self.append_field(field) - return field - def add_media(self): """adds media (CSS & JS) required by this widget""" if self.needs_js: