def widget(field, value, download_url=None, **attributes):
        """
        generates a INPUT file tag.

        Optionally provides an A link to the file, including a checkbox so
        the file can be deleted.
        All is wrapped in a DIV.

        see also: :meth:`FormWidget.widget`

        :param download_url: Optional URL to link to the file (default = None)
        """

        default = dict(_type='file', )
        attr = UploadWidget._attributes(field, default, **attributes)

        inp = INPUT(**attr)

        if download_url and value:
            url = download_url + '/' + value
            (br, image) = ('', '')
            if UploadWidget.is_image(value):
                br = BR()
                image = IMG(_src=url, _width=UploadWidget.DEFAULT_WIDTH)
            inp = DIV(
                inp, '[', A(UploadWidget.GENERIC_DESCRIPTION, _href=url), '|',
                INPUT(_type='checkbox',
                      _name=field.name + UploadWidget.ID_DELETE_SUFFIX),
                'delete]', br, image)
        return inp
 def __call__(self, field, value, **attributes):
     default = dict(
         _type='text',
         value=(value != None and str(value)) or '',
     )
     attr = StringWidget._attributes(field, default, **attributes)
     div_id = self.keyword + '_div'
     attr['_autocomplete'] = 'off'
     if self.is_reference:
         key2 = self.keyword + '_aux'
         key3 = self.keyword + '_auto'
         attr['_class'] = 'string'
         name = attr['_name']
         if 'requires' in attr: del attr['requires']
         attr['_name'] = key2
         value = attr['value']
         record = self.db(self.fields[1] == value).select(
             self.fields[0]).first()
         attr['value'] = record and record[self.fields[0].name]
         attr['_onblur']="jQuery('#%(div_id)s').delay(3000).fadeOut('slow');" % \
             dict(div_id=div_id,u='F'+self.keyword)
         attr['_onkeyup'] = "jQuery('#%(key3)s').val('');var e=event.which?event.which:event.keyCode; function %(u)s(){jQuery('#%(id)s').val(jQuery('#%(key)s :selected').text());jQuery('#%(key3)s').val(jQuery('#%(key)s').val())}; if(e==39) %(u)s(); else if(e==40) {if(jQuery('#%(key)s option:selected').next().length)jQuery('#%(key)s option:selected').attr('selected',null).next().attr('selected','selected'); %(u)s();} else if(e==38) {if(jQuery('#%(key)s option:selected').prev().length)jQuery('#%(key)s option:selected').attr('selected',null).prev().attr('selected','selected'); %(u)s();} else if(jQuery('#%(id)s').val().length>=%(min_length)s) jQuery.get('%(url)s?%(key)s='+escape(jQuery('#%(id)s').val()),function(data){if(data=='')jQuery('#%(key3)s').val('');else{jQuery('#%(id)s').next('.error').hide();jQuery('#%(div_id)s').html(data).show().focus();jQuery('#%(div_id)s select').css('width',jQuery('#%(id)s').css('width'));jQuery('#%(key3)s').val(jQuery('#%(key)s').val());jQuery('#%(key)s').change(%(u)s);jQuery('#%(key)s').click(%(u)s);};}); else jQuery('#%(div_id)s').fadeOut('slow');" % \
             dict(url=self.url,min_length=self.min_length,
                  key=self.keyword,id=attr['_id'],key2=key2,key3=key3,
                  name=name,div_id=div_id,u='F'+self.keyword)
         return TAG[''](INPUT(**attr),
                        INPUT(_type='hidden',
                              _id=key3,
                              _value=value,
                              _name=name,
                              requires=field.requires),
                        DIV(_id=div_id, _style='position:absolute;'))
     else:
         attr['_name'] = field.name
         attr['_onblur']="jQuery('#%(div_id)s').delay(3000).fadeOut('slow');" % \
             dict(div_id=div_id,u='F'+self.keyword)
         attr['_onkeyup'] = "var e=event.which?event.which:event.keyCode; function %(u)s(){jQuery('#%(id)s').val(jQuery('#%(key)s').val())}; if(e==39) %(u)s(); else if(e==40) {if(jQuery('#%(key)s option:selected').next().length)jQuery('#%(key)s option:selected').attr('selected',null).next().attr('selected','selected'); %(u)s();} else if(e==38) {if(jQuery('#%(key)s option:selected').prev().length)jQuery('#%(key)s option:selected').attr('selected',null).prev().attr('selected','selected'); %(u)s();} else if(jQuery('#%(id)s').val().length>=%(min_length)s) jQuery.get('%(url)s?%(key)s='+escape(jQuery('#%(id)s').val()),function(data){jQuery('#%(id)s').next('.error').hide();jQuery('#%(div_id)s').html(data).show().focus();jQuery('#%(div_id)s select').css('width',jQuery('#%(id)s').css('width'));jQuery('#%(key)s').change(%(u)s);jQuery('#%(key)s').click(%(u)s);}); else jQuery('#%(div_id)s').fadeOut('slow');" % \
             dict(url=self.url,min_length=self.min_length,
                  key=self.keyword,id=attr['_id'],div_id=div_id,u='F'+self.keyword)
         return TAG[''](INPUT(**attr),
                        DIV(_id=div_id, _style='position:absolute;'))
    def widget(field, value, **attributes):
        """
        generates an INPUT text tag.

        see also: :meth:`FormWidget.widget`
        """

        default = dict(
            _type='text',
            value=(value != None and str(value)) or '',
        )
        attr = StringWidget._attributes(field, default, **attributes)

        return INPUT(**attr)
    def widget(field, value, **attributes):
        """
        generates an INPUT checkbox tag.

        see also: :meth:`FormWidget.widget`
        """

        default = dict(
            _type='checkbox',
            value=value,
        )
        attr = BooleanWidget._attributes(field, default, **attributes)

        return INPUT(**attr)
    def widget(field, value, **attributes):
        """
        generates a INPUT password tag.
        If a value is present it will be shown as a number of '*', not related
        to the length of the actual value.

        see also: :meth:`FormWidget.widget`
        """

        default = dict(
            _type='password',
            _value=(value and PasswordWidget.DEFAULT_PASSWORD_DISPLAY) or '',
        )
        attr = PasswordWidget._attributes(field, default, **attributes)

        return INPUT(**attr)
示例#6
0
    def widget(field, value, **attributes):
        """
        generates an INPUT text tag.

        see also: :meth:`FormWidget.widget`
        """
        if value:
            if isinstance(value, unicode):
                value = value.encode("utf-8")
        default = dict(
            _type='hidden',
            value=(value != None and str(value)) or '',
        )
        attr = StringWidget._attributes(field, default, **attributes)

        return INPUT(**attr)
    def widget(field, value, **attributes):
        """
        generates a TABLE tag, including INPUT checkboxes (multiple allowed)

        see also: :meth:`FormWidget.widget`
        """

        # was values = re.compile('[\w\-:]+').findall(str(value))
        values = not isinstance(value, (list, tuple)) and [value] or value

        attr = OptionsWidget._attributes(field, {}, **attributes)

        requires = field.requires
        if not isinstance(requires, (list, tuple)):
            requires = [requires]
        if requires:
            if hasattr(requires[0], 'options'):
                options = requires[0].options()
            else:
                raise SyntaxError, 'widget cannot determine options of %s' \
                    % field

        options = [(k, v) for k, v in options if k != '']
        opts = []
        cols = attributes.get('cols', 1)
        totals = len(options)
        mods = totals % cols
        rows = totals / cols
        if mods:
            rows += 1

        for r_index in range(rows):
            tds = []
            for k, v in options[r_index * cols:(r_index + 1) * cols]:
                tds.append(
                    TD(
                        INPUT(_type='checkbox',
                              _name=field.name,
                              requires=attr.get('requires', None),
                              hideerror=True,
                              _value=k,
                              value=(k in values)), v))
            opts.append(TR(tds))

        if opts:
            opts[-1][0][0]['hideerror'] = False
        return TABLE(*opts, **attr)
    def widget(field, value, **attributes):
        _id = '%s_%s' % (field._tablename, field.name)
        _name = field.name
        if field.type == 'list:integer': _class = 'integer'
        else: _class = 'string'
        items=[LI(INPUT(_id=_id,_class=_class,_name=_name,value=v,hideerror=True)) \
                   for v in value or ['']]
        script = SCRIPT("""
// from http://refactormycode.com/codes/694-expanding-input-list-using-jquery
(function(){
jQuery.fn.grow_input = function() {
  return this.each(function() {
    var ul = this;
    jQuery(ul).find(":text").keypress(function (e) { return (e.which == 13) ? pe(ul) : true; });
  });
};
function pe(ul) {
  var new_line = ml(ul);
  rel(ul);
  new_line.appendTo(ul);
  new_line.find(":text").focus();
  return false;
}
function ml(ul) {
  var line = jQuery(ul).find("li:first").clone(true);
  line.find(':text').val('');
  return line;
}
function rel(ul) {
  jQuery(ul).find("li").each(function() {
    var trimmed = jQuery.trim(jQuery(this.firstChild).val());
    if (trimmed=='') jQuery(this).remove(); else jQuery(this.firstChild).val(trimmed);
  });
}
})();
jQuery(document).ready(function(){jQuery('#%s_grow_input').grow_input();});
""" % _id)
        attributes['_id'] = _id + '_grow_input'
        return TAG[''](UL(*items, **attributes), script)
    def __init__(self,
                 table,
                 record=None,
                 deletable=False,
                 linkto=None,
                 upload=None,
                 fields=None,
                 labels=None,
                 col3={},
                 submit_button='Submit',
                 delete_label='Check to delete:',
                 showid=True,
                 readonly=False,
                 comments=True,
                 keepopts=[],
                 ignore_rw=False,
                 record_id=None,
                 formstyle='table3cols',
                 **attributes):
        """
        SQLFORM(db.table,
               record=None,
               fields=['name'],
               labels={'name': 'Your name'},
               linkto=URL(r=request, f='table/db/')
        """

        self.ignore_rw = ignore_rw
        self.formstyle = formstyle
        nbsp = XML(' ')  # Firefox2 does not display fields with blanks
        FORM.__init__(self, *[], **attributes)
        ofields = fields
        keyed = hasattr(table, '_primarykey')

        # if no fields are provided, build it from the provided table
        # will only use writable or readable fields, unless forced to ignore
        if fields == None:
            fields = [
                f.name for f in table
                if (ignore_rw or f.writable or f.readable) and not f.compute
            ]
        self.fields = fields

        # make sure we have an id
        if self.fields[0] != table.fields[0] and \
                isinstance(table,Table) and not keyed:
            self.fields.insert(0, table.fields[0])

        self.table = table

        # try to retrieve the indicated record using its id
        # otherwise ignore it
        if record and isinstance(record, (int, long, str, unicode)):
            if not str(record).isdigit():
                raise HTTP(404, "Object not found")
            record = table._db(table.id == record).select().first()
            if not record:
                raise HTTP(404, "Object not found")
        self.record = record

        self.record_id = record_id
        if keyed:
            if record:
                self.record_id = dict([(k, record[k])
                                       for k in table._primarykey])
            else:
                self.record_id = dict([(k, None) for k in table._primarykey])
        self.field_parent = {}
        xfields = []
        self.fields = fields
        self.custom = Storage()
        self.custom.dspval = Storage()
        self.custom.inpval = Storage()
        self.custom.label = Storage()
        self.custom.comment = Storage()
        self.custom.widget = Storage()
        self.custom.linkto = Storage()

        for fieldname in self.fields:
            if fieldname.find('.') >= 0:
                continue

            field = self.table[fieldname]
            comment = None

            if comments:
                comment = col3.get(fieldname, field.comment)
            if comment == None:
                comment = ''
            self.custom.comment[fieldname] = comment

            if labels != None and fieldname in labels:
                label = labels[fieldname]
                colon = ''
            else:
                label = field.label
                colon = ': '
            self.custom.label[fieldname] = label

            field_id = '%s_%s' % (table._tablename, fieldname)

            label = LABEL(label,
                          colon,
                          _for=field_id,
                          _id=field_id + SQLFORM.ID_LABEL_SUFFIX)

            row_id = field_id + SQLFORM.ID_ROW_SUFFIX
            if field.type == 'id':
                self.custom.dspval.id = nbsp
                self.custom.inpval.id = ''
                widget = ''
                if record:
                    if showid and 'id' in fields and field.readable:
                        v = record['id']
                        widget = SPAN(v, _id=field_id)
                        self.custom.dspval.id = str(v)
                        xfields.append((row_id, label, widget, comment))
                    self.record_id = str(record['id'])
                self.custom.widget.id = widget
                continue

            if readonly and not ignore_rw and not field.readable:
                continue

            if record:
                default = record[fieldname]
            else:
                default = field.default

            cond = readonly or \
                (not ignore_rw and not field.writable and field.readable)

            if default and not cond:
                default = field.formatter(default)
            dspval = default
            inpval = default

            if cond:

                # ## if field.represent is available else
                # ## ignore blob and preview uploaded images
                # ## format everything else

                if field.represent:
                    inp = field.represent(default)
                elif field.type in ['blob']:
                    continue
                elif field.type == 'upload':
                    inp = UploadWidget.represent(field, default, upload)
                else:
                    inp = field.formatter(default)
            elif hasattr(field, 'widget') and field.widget:
                inp = field.widget(field, default)
            elif field.type == 'upload':
                inp = self.widgets.upload.widget(field, default, upload)
            elif field.type == 'boolean':
                inp = self.widgets.boolean.widget(field, default)
                if default:
                    inpval = 'checked'
                else:
                    inpval = ''
            elif OptionsWidget.has_options(field):
                if not field.requires.multiple:
                    inp = self.widgets.options.widget(field, default)
                else:
                    inp = self.widgets.multiple.widget(field, default)
                if fieldname in keepopts:
                    inpval = TAG[''](*inp.components)
            elif str(field.type).startswith('list'):
                inp = self.widgets.list.widget(field, default)
            elif field.type == 'text':
                inp = self.widgets.text.widget(field, default)
            elif field.type == 'password':
                inp = self.widgets.password.widget(field, default)
                if self.record:
                    dspval = PasswordWidget.DEFAULT_PASSWORD_DISPLAY
                else:
                    dspval = ''
            elif field.type == 'blob':
                continue
            else:
                inp = self.widgets.string.widget(field, default)

            xfields.append((row_id, label, inp, comment))
            self.custom.dspval[fieldname] = dspval or nbsp
            self.custom.inpval[fieldname] = inpval or ''
            self.custom.widget[fieldname] = inp

        # if a record is provided and found, as is linkto
        # build a link
        if record and linkto:
            for (rtable, rfield) in table._referenced_by:
                if keyed:
                    rfld = table._db[rtable][rfield]
                    query = urllib.quote(
                        str(rfld == record[rfld.type[10:].split('.')[1]]))
                else:
                    #                 <block>
                    query = urllib.quote(
                        str(table._db[rtable][rfield] == record.id))
                lname = olname = '%s.%s' % (rtable, rfield)
                if ofields and not olname in ofields:
                    continue
                if labels and lname in labels:
                    lname = labels[lname]
                widget = A(lname,
                           _class='reference',
                           _href='%s/%s?query=%s' % (linkto, rtable, query))
                xfields.append(
                    (olname.replace('.', '__') + SQLFORM.ID_ROW_SUFFIX, '',
                     widget, col3.get(olname, '')))
                self.custom.linkto[olname.replace('.', '__')] = widget


#                 </block>

# when deletable, add delete? checkbox
        self.custom.deletable = ''
        if record and deletable:
            widget = INPUT(
                _type='checkbox',
                _class='delete',
                _id=self.FIELDKEY_DELETE_RECORD,
                _name=self.FIELDNAME_REQUEST_DELETE,
            )
            xfields.append(
                (self.FIELDKEY_DELETE_RECORD + SQLFORM.ID_ROW_SUFFIX,
                 LABEL(delete_label,
                       _for=self.FIELDKEY_DELETE_RECORD,
                       _id=self.FIELDKEY_DELETE_RECORD +
                       SQLFORM.ID_LABEL_SUFFIX), widget,
                 col3.get(self.FIELDKEY_DELETE_RECORD, '')))
            self.custom.deletable = widget
        # when writable, add submit button
        self.custom.submit = ''
        if not readonly:
            widget = INPUT(_type='submit', _value=submit_button)
            xfields.append(
                ('submit_record' + SQLFORM.ID_ROW_SUFFIX, '', widget,
                 col3.get('submit_button', '')))
            self.custom.submit = widget
        # if a record is provided and found
        # make sure it's id is stored in the form
        if record:
            if not self['hidden']:
                self['hidden'] = {}
            if not keyed:
                self['hidden']['id'] = record['id']

        (begin, end) = self._xml()
        self.custom.begin = XML("<%s %s>" % (self.tag, begin))
        self.custom.end = XML("%s</%s>" % (end, self.tag))
        if formstyle == 'table3cols':
            table = TABLE()
            for id, a, b, c in xfields:
                td_b = self.field_parent[id] = TD(b, _class='w2p_fw')
                table.append(
                    TR(TD(a, _class='w2p_fl'),
                       td_b,
                       TD(c, _class='w2p_fc'),
                       _id=id))
        elif formstyle == 'table2cols':
            table = TABLE()
            for id, a, b, c in xfields:
                td_b = self.field_parent[id] = TD(b,
                                                  _class='w2p_fw',
                                                  _colspan="2")
                table.append(
                    TR(TD(a, _class='w2p_fl'),
                       TD(c, _class='w2p_fc'),
                       _id=id + '1',
                       _class='even'))
                table.append(TR(td_b, _id=id + '2', _class='odd'))
        elif formstyle == 'divs':
            table = TAG['']()
            for id, a, b, c in xfields:
                div_b = self.field_parent[id] = DIV(b, _class='w2p_fw')
                table.append(
                    DIV(DIV(a, _class='w2p_fl'),
                        div_b,
                        DIV(c, _class='w2p_fc'),
                        _id=id))
        elif formstyle == 'ul':
            table = UL()
            for id, a, b, c in xfields:
                div_b = self.field_parent[id] = DIV(b, _class='w2p_fw')
                table.append(
                    LI(DIV(a, _class='w2p_fl'),
                       div_b,
                       DIV(c, _class='w2p_fc'),
                       _id=id))
        elif type(formstyle) == type(lambda: None):
            table = TABLE()
            for id, a, b, c in xfields:
                td_b = self.field_parent[id] = TD(b, _class='w2p_fw')
                newrows = formstyle(id, a, td_b, c)
                if type(newrows).__name__ != "tuple":
                    newrows = [newrows]
                for newrow in newrows:
                    table.append(newrow)
        else:
            raise RuntimeError, 'formsyle not supported'
        self.components = [table]
示例#10
0
    def __init__(self,
                 table,
                 record=None,
                 deletable=False,
                 download="",
                 upload=None,
                 fields=None,
                 labels=None,
                 col3={},
                 submit_button='Submit',
                 delete_label='Check to delete:',
                 showid=True,
                 readonly=False,
                 comments=True,
                 keepopts=[],
                 ignore_rw=False,
                 formstyle='divs',
                 record_pk_name='_id',
                 tabs=[],
                 **attributes):
        """
        FORMBUILDER(db.table,
               record=None,
               fields=['name'],
               labels={'name': 'Your name'},
        """
        self.table = table
        if record:
            for itm in record:
                if isinstance(record[itm], (list, tuple)):
                    if not (itm in self.table.fields
                            and self.table[itm].type.startswith("list::")):
                        record[itm] = record[itm][-1]
        self.custom_file = upload
        self.ignore_rw = ignore_rw
        self.formstyle = formstyle
        self.record_pk_name = record_pk_name
        nbsp = XML('&nbsp;')  # Firefox2 does not display fields with blanks
        attributes.update({"_id": "hyform_%s" % self.table._tablename})
        FORM.__init__(self, *[], **attributes)
        ofields = fields

        # if no fields are provided, build it from the provided table
        # will only use writable or readable fields, unless forced to ignore
        if fields == None:
            fields = [
                f.name for f in table
                if (ignore_rw or f.writable or f.readable)
            ]

        self.record = record

        self.field_parent = {}
        xfields = {}
        xfields_keys = []
        self.fields = fields
        self.custom = Storage()
        self.custom.dspval = Storage()
        self.custom.inpval = Storage()
        self.custom.label = Storage()
        self.custom.comment = Storage()
        self.custom.widget = Storage()

        for fieldname in self.fields:
            if fieldname.find('.') >= 0:
                continue

            field = self.table[fieldname]
            comment = None

            if comments:
                comment = col3.get(fieldname, field.comment)
            if comment == None:
                comment = ''
            self.custom.comment[fieldname] = comment

            if labels != None and fieldname in labels:
                label = labels[fieldname]
                colon = ''
            else:
                label = field.label
                colon = ': '
            self.custom.label[fieldname] = label

            field_id = '%s_%s' % (table._tablename, fieldname)

            label = LABEL(label,
                          colon,
                          _for=field_id,
                          _id=field_id + FORMBUILDER.ID_LABEL_SUFFIX)

            row_id = field_id + FORMBUILDER.ID_ROW_SUFFIX

            if readonly and not ignore_rw and not field.readable:
                continue

            if record:
                default = record.get(fieldname, field.default)
            else:
                default = field.default

            cond = readonly or \
                (not ignore_rw and not field.writable and field.readable)

            if default and not cond:
                default = field.formatter(default)
            dspval = default
            inpval = default

            if cond:

                # ## if field.represent is available else
                # ## ignore blob and preview uploaded images
                # ## format everything else

                if field.represent:
                    inp = field.represent(default)
                elif field.type in ['blob']:
                    continue
                elif field.type == 'upload':
                    inp = UploadWidget.represent(field, default, download)
                elif field.type == 'boolean':
                    inp = self.widgets.boolean.widget(field,
                                                      default,
                                                      _disabled=True)
                else:
                    inp = field.formatter(default)
            elif field.type == 'upload':
                if hasattr(field, 'widget') and field.widget:
                    inp = field.widget(field, default, download)
                else:
                    inp = self.widgets.upload.widget(field, default, download)
            elif hasattr(field, 'widget') and field.widget:
                inp = field.widget(field, default)
            elif field.type == 'boolean':
                inp = self.widgets.boolean.widget(field, default)
                if default:
                    inpval = 'checked'
                else:
                    inpval = ''
            elif OptionsWidget.has_options(field):
                if not field.requires.multiple:
                    inp = self.widgets.options.widget(field, default)
                else:
                    inp = self.widgets.multiple.widget(field, default)
                if fieldname in keepopts:
                    inpval = TAG[''](*inp.components)
            elif field.type.startswith('list:'):
                inp = self.widgets.list.widget(field, default)
            elif field.type == 'text':
                inp = self.widgets.text.widget(field, default)
            elif field.type == 'hidden':
                inp = self.widgets.hidden.widget(field, default)
            elif field.type == 'password':
                inp = self.widgets.password.widget(field, default)
                if self.record:
                    dspval = PasswordWidget.DEFAULT_PASSWORD_DISPLAY
                else:
                    dspval = ''
            elif field.type == 'blob':
                continue
            else:
                inp = self.widgets.string.widget(field, default)
            xfields_keys.append(fieldname)
            xfields[fieldname] = (row_id, label, inp, comment)
            self.custom.dspval[fieldname] = dspval or nbsp
            self.custom.inpval[fieldname] = inpval or ''
            self.custom.widget[fieldname] = inp

        # when writable, add submit button
        self.custom.submit = ''
        if not readonly:
            widget = INPUT(_type='submit',
                           _class="submit",
                           _value=submit_button,
                           _id="submit_%s" % self.table._tablename)
            self.custom.submit = widget
        # if a record is provided and found
        # make sure it's id is stored in the form
        if record:
            if not self['hidden']:
                self['hidden'] = {}

        (begin, end) = self._xml()
        self.custom.begin = XML("<%s %s>" % (self.tag, begin))
        self.custom.end = XML("%s</%s>" % (end, self.tag))

        table = TAG['']()
        if formstyle == 'divs':
            # tabs = [
            #          {"tabname":"personal","caption":"个人信息","fields":["login","age"]},
            #          {"tabname":"comp","caption":"公司信息","fields":["login","age"]},
            #          {"tabname":"gf","caption":"朋友信息","fields":["login","age"]},
            #        ]
            if tabs:
                tab_headers = UL()
                tab_bodys = []

                for i, tab in enumerate(tabs):
                    tbody = DIV(_id="body_%s" % (tab.get("tabname", "")))
                    if i == 0:
                        li = LI(tab.get("caption", ""),
                                _class="active",
                                _id="tab_%s" % (tab.get("tabname", "")))
                        tbody['_class'] = "active"
                    else:
                        li = LI(tab.get("caption", ""),
                                _style="display:none;",
                                _id="tab_%s" % (tab.get("tabname", "")))
                        tbody['_style'] = "display:none;"
                    tab_headers.append(li)
                    for f in tab.get("fields", []):
                        if f in xfields_keys:
                            id, a, b, c = xfields[f]
                            div_b = self.field_parent[id] = DIV(
                                b, _class='w2p_fw')
                            tbody.append(
                                DIV(DIV(a, _class='w2p_fl'),
                                    div_b,
                                    DIV(c, _class='w2p_fc'),
                                    _id=id,
                                    _class="fb_row"))
                    tab_bodys.append(tbody)
                tab_headers = DIV(tab_headers,
                                  _id="tab_headers_%s" % self.table._tablename)
                tab_bodys = DIV(tab_bodys,
                                _id="tab_bodys_%s" % self.table._tablename)
                table.append(DIV(tab_headers, tab_bodys))
            else:
                for d in xfields_keys:
                    id, a, b, c = xfields[d]
                    div_b = self.field_parent[id] = DIV(b, _class='w2p_fw')
                    table.append(
                        DIV(DIV(a, _class='w2p_fl'),
                            div_b,
                            DIV(c, _class='w2p_fc'),
                            _id=id,
                            _class="fb_row"))

        self.components = [table, self.custom.submit]